aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--.gitpod.Dockerfile10
-rw-r--r--README.md60
-rw-r--r--arbiter/.golangci.yml18
-rw-r--r--arbiter/Dockerfile18
-rw-r--r--arbiter/arbiter.go505
-rw-r--r--arbiter/go.mod17
-rw-r--r--arbiter/go.sum41
-rwxr-xr-xarbiter/tests.sh4
-rw-r--r--docker-compose-test.yaml100
-rw-r--r--docker-compose.yaml119
-rw-r--r--hived/Dockerfile14
-rw-r--r--hived/alertHandlers.go8
-rwxr-xr-xhived/docker-entrypoint.sh6
-rw-r--r--hived/go.mod100
-rw-r--r--hived/go.sum346
-rw-r--r--hived/hived.go591
-rw-r--r--hived/hived.toml7
-rw-r--r--nginx.conf35
-rw-r--r--telebot/.golangci.yml19
-rw-r--r--telebot/Dockerfile21
-rwxr-xr-xtelebot/docker-entrypoint.sh6
-rw-r--r--telebot/go.mod22
-rw-r--r--telebot/go.sum149
-rw-r--r--telebot/telebot.go167
-rwxr-xr-xtest/endpoints.sh35
25 files changed, 973 insertions, 1445 deletions
diff --git a/.gitpod.Dockerfile b/.gitpod.Dockerfile
deleted file mode 100644
index 1810344..0000000
--- a/.gitpod.Dockerfile
+++ /dev/null
@@ -1,10 +0,0 @@
-FROM alpine:3.13 as builder
-
-RUN apk update && apk upgrade
-RUN apk add go git
-COPY go.* /hived/
-RUN cd /hived && go mod download
-COPY *.go /hived/
-RUN cd /hived && go build
-COPY ./docker-entrypoint.sh /hived/
-# ENTRYPOINT ["/hived/docker-entrypoint.sh"]
diff --git a/README.md b/README.md
index 8542d55..7bfb97d 100644
--- a/README.md
+++ b/README.md
@@ -3,62 +3,4 @@
# hived
-`hived` is small personal cryptocurrency server:<br/>
-
-- It sends notifications through telegram.<br/>
-
-Currently it has 5 endpoint:<br/>
-
-### /price
-
-Lets you ask for the price of the currency. You can determine the currency the value is returned in.<br/>
-
-### /pair
-
-Takes in a pair of currencies and a multiplier. Determines and returns the ratio.<br/>
-
-### /alert
-
-#### POST
-
-Takes in a name and a math expression containing the names of the currencies. Checks the expression periodically. Sends a message over telegram when the expression holds true.<br/>
-The expression's result must be boolean. As an example:<br/>
-
-```Go
-ETH*50>50000.
-ETH*60/(DOGE*300000) < 4.
-```
-
-You can have as many parameters as you like. The requests for the crypto prices are all turned into individual goroutines so it's fast.<br/>
-The expression evaluation is powered by [govaluate](https://github.com/Knetic/govaluate). So for a set of rules and what you can and cannot do please check the documentation over there.<br/>
-
-#### DELETE
-
-Deletes the key from the DB so you will no longer receive updates.<br/>
-
-#### PUT
-
-Updates the alert.<br/.>
-
-#### GET
-
-Fetch the alert with the given name.<br/>
-
-### /ex
-
-Gets the list of currencies that are available to be traded.<br/>
-
-You can check under `./test` for some examples of curl commands.<br/>
-
-### /health
-
-Returns the health status of the service.<br/>
-
-```sh
-TELEGRAM_BOT_TOKEN="my-telegram-bot-api-key"
-```
-
-If you want to use docker-compose, it's as simple as running `docker-compose up`. You just need to provide the files. You can check the file names in the docker-compose file.<br/>
-Both the server itself and the redis image are alpine-based so they're pretty small.<br/>
-
-## telebot
+`hived` is a cryptocurrency api server:<br/>
diff --git a/arbiter/.golangci.yml b/arbiter/.golangci.yml
deleted file mode 100644
index 878e031..0000000
--- a/arbiter/.golangci.yml
+++ /dev/null
@@ -1,18 +0,0 @@
-run:
- concurrency: 16
- timeout: 5m
- modules-download-mode: readonly
- allow-parallel-runners: true
- allow-serial-runners: true
- go: '1.22'
-linters-settings:
- depguard:
- rules:
- srcs:
- listMode: "Strict"
- allow:
- - $gostd
- - github.com/go-redis/redis/v8
- - github.com/gorilla/mux
- - github.com/rs/zerolog
- - github.com/rs/zerolog/log
diff --git a/arbiter/Dockerfile b/arbiter/Dockerfile
deleted file mode 100644
index a512798..0000000
--- a/arbiter/Dockerfile
+++ /dev/null
@@ -1,18 +0,0 @@
-FROM golang:1.22-alpine3.19 as builder
-RUN apk update && apk upgrade
-RUN apk add go git
-COPY go.* /arbiter/
-RUN cd /arbiter && go mod download
-COPY *.go /arbiter/
-RUN cd /arbiter && go build
-
-FROM alpine:3.19 as certbuilder
-RUN apk add openssl
-WORKDIR /certs
-RUN openssl req -nodes -new -x509 -subj="/C=US/ST=Denial/L=springfield/O=Dis/CN=localhost" -keyout server.key -out server.cert
-
-# FROM gcr.io/distroless/static-debian10
-FROM alpine:3.19
-COPY --from=certbuilder /certs /certs
-COPY --from=builder /arbiter/arbiter /arbiter/
-ENTRYPOINT ["/arbiter/arbiter"]
diff --git a/arbiter/arbiter.go b/arbiter/arbiter.go
deleted file mode 100644
index 6145a45..0000000
--- a/arbiter/arbiter.go
+++ /dev/null
@@ -1,505 +0,0 @@
-package main
-
-import (
- "context"
- "crypto/tls"
- "encoding/json"
- "errors"
- "flag"
- "io"
- "net/http"
- "net/url"
- "os"
- "os/signal"
- "strconv"
- "sync"
- "time"
-
- "github.com/go-redis/redis/v8"
- "github.com/gorilla/mux"
- "github.com/rs/zerolog"
- "github.com/rs/zerolog/log"
-)
-
-var (
- errBadLogic = errors.New("we should not be here")
- errUnexpectedParam = errors.New("got unexpected parameter")
- errUnknownDeployment = errors.New("unknown deployment kind")
-)
-
-const (
- serverDeploymentType = "SERVER_DEPLOYMENT_TYPE"
- coingeckoAPIURLv3 = "https://api.coingecko.com/api/v3"
- coincapAPIURLv2 = "https://api.coincap.io/v2"
- getTimeout = 5
- httpClientTimeout = 5
- serverTLSReadTimeout = 15
- serverTLSWriteTimeout = 15
- defaultGracefulShutdown = 15
-)
-
-// https://docs.coincap.io/
-type CoinCapAssetGetResponseData struct {
- ID string `json:"id"`
- Rank string `json:"rank"`
- Symbol string `json:"symbol"`
- Name string `json:"name"`
- Supply string `json:"supply"`
- MaxSupply string `json:"maxSupply"`
- MarketCapUsd string `json:"marketCapUsd"`
- VolumeUsd24Hr string `json:"volumeUsd24Hr"`
- PriceUsd string `json:"priceUsd"`
- ChangePercent24Hr string `json:"changePercent24Hr"`
- Vwap24Hr string `json:"vwap24Hr"`
-}
-
-type priceResponseData struct {
- Name string `json:"name"`
- Price float64 `json:"price"`
- Unit string `json:"unit"`
- Err string `json:"err"`
- IsSuccessful bool `json:"isSuccessful"`
-}
-
-type CoinCapAssetGetResponse struct {
- Data CoinCapAssetGetResponseData `json:"data"`
- TimeStamp int64 `json:"timestamp"`
-}
-
-type HTTPHandlerFunc func(http.ResponseWriter, *http.Request)
-
-type HTTPHandler struct {
- name string
- function HTTPHandlerFunc
-}
-
-type priceChanStruct struct {
- name string
- price float64
-}
-
-type errorChanStruct struct {
- hasError bool
- err error
-}
-
-func GetProxiedClient() *http.Client {
- transport := &http.Transport{
- DisableKeepAlives: true,
- Proxy: http.ProxyFromEnvironment,
- }
- client := &http.Client{
- Transport: transport,
- Timeout: httpClientTimeout * time.Second,
- CheckRedirect: nil,
- Jar: nil,
- }
-
- return client
-}
-
-// OWASP: https://cheatsheetseries.owasp.org/cheatsheets/REST_Security_Cheat_Sheet.html
-func addSecureHeaders(writer *http.ResponseWriter) {
- (*writer).Header().Set("Cache-Control", "no-store")
- (*writer).Header().Set("Content-Security-Policy", "default-src https;")
- (*writer).Header().Set("Strict-Transport-Security", "max-age=63072000;")
- (*writer).Header().Set("X-Content-Type-Options", "nosniff")
- (*writer).Header().Set("X-Frame-Options", "DENY")
- (*writer).Header().Set("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS")
-}
-
-func getPriceFromCoinGecko(
- ctx context.Context,
- name, unit string,
- wg *sync.WaitGroup,
- priceChan chan<- priceChanStruct,
- errChan chan<- errorChanStruct,
-) {
- defer wg.Done()
-
- priceFloat := 0.
-
- params := "/simple/price?ids=" + url.QueryEscape(name) + "&" +
- "vs_currencies=" + url.QueryEscape(unit)
- path := coingeckoAPIURLv3 + params
-
- client := GetProxiedClient()
-
- req, err := http.NewRequestWithContext(ctx, http.MethodGet, path, nil)
- if err != nil {
- priceChan <- priceChanStruct{name: name, price: priceFloat}
- errChan <- errorChanStruct{hasError: true, err: err}
-
- log.Error().Err(err)
-
- return
- }
-
- resp, err := client.Do(req)
- if err != nil {
- priceChan <- priceChanStruct{name: name, price: priceFloat}
- errChan <- errorChanStruct{hasError: true, err: err}
-
- log.Error().Err(err).Send()
-
- return
- }
- defer resp.Body.Close()
-
- body, err := io.ReadAll(resp.Body)
- if err != nil {
- priceChan <- priceChanStruct{name: name, price: priceFloat}
- errChan <- errorChanStruct{hasError: true, err: err}
-
- log.Error().Err(err).Send()
- }
-
- jsonBody := make(map[string]interface{})
-
- err = json.Unmarshal(body, &jsonBody)
- if err != nil {
- priceChan <- priceChanStruct{name: name, price: priceFloat}
- errChan <- errorChanStruct{hasError: true, err: err}
-
- log.Error().Err(err).Send()
- }
-
- price, isOk := jsonBody[name].(map[string]interface{})
- if !isOk {
- priceChan <- priceChanStruct{name: name, price: priceFloat}
- errChan <- errorChanStruct{hasError: true, err: err}
-
- log.Error().Err(err)
-
- return
- }
-
- log.Info().Msg(string(body))
-
- priceFloat, isOk = price[unit].(float64)
- if !isOk {
- priceChan <- priceChanStruct{name: name, price: priceFloat}
- errChan <- errorChanStruct{hasError: true, err: err}
-
- log.Error().Err(err)
-
- return
- }
-
- priceChan <- priceChanStruct{name: name, price: priceFloat}
- errChan <- errorChanStruct{hasError: false, err: nil}
-}
-
-func getPriceFromCoinCap(
- ctx context.Context,
- name string,
- wg *sync.WaitGroup,
- priceChan chan<- priceChanStruct,
- errChan chan<- errorChanStruct,
-) {
- defer wg.Done()
-
- priceFloat := 0.
-
- params := "/assets/" + url.QueryEscape(name)
- path := coincapAPIURLv2 + params
-
- client := GetProxiedClient()
-
- req, err := http.NewRequestWithContext(ctx, http.MethodGet, path, nil)
- if err != nil {
- priceChan <- priceChanStruct{name: name, price: priceFloat}
- errChan <- errorChanStruct{hasError: true, err: err}
-
- log.Error().Err(err)
-
- return
- }
-
- resp, err := client.Do(req)
- if err != nil {
- priceChan <- priceChanStruct{name: name, price: priceFloat}
- errChan <- errorChanStruct{hasError: true, err: err}
-
- log.Error().Err(err).Send()
-
- return
- }
- defer resp.Body.Close()
-
- body, err := io.ReadAll(resp.Body)
- if err != nil {
- priceChan <- priceChanStruct{name: name, price: priceFloat}
- errChan <- errorChanStruct{hasError: true, err: err}
-
- log.Error().Err(err)
- }
-
- var coinCapAssetGetResponse CoinCapAssetGetResponse
-
- err = json.Unmarshal(body, &coinCapAssetGetResponse)
- if err != nil {
- priceChan <- priceChanStruct{name: name, price: priceFloat}
- errChan <- errorChanStruct{hasError: true, err: err}
-
- log.Error().Err(err).Send()
- }
-
- priceFloat, err = strconv.ParseFloat(coinCapAssetGetResponse.Data.PriceUsd, 64)
- if err != nil {
- priceChan <- priceChanStruct{name: name, price: priceFloat}
- errChan <- errorChanStruct{hasError: true, err: err}
-
- log.Error().Err(err).Send()
- }
-
- log.Info().Msg(string(body))
-
- priceChan <- priceChanStruct{name: name, price: priceFloat}
- errChan <- errorChanStruct{hasError: false, err: nil}
-}
-
-func arbHandler(w http.ResponseWriter, r *http.Request) {
- w.Header().Add("Content-Type", "application/json")
-
- if r.Method != http.MethodGet {
- http.Error(w, "Method is not supported.", http.StatusNotFound)
- }
-
- addSecureHeaders(&w)
-
- var name string
-
- var unit string
-
- params := r.URL.Query()
- for key, value := range params {
- switch key {
- case "name":
- name = value[0]
- case "unit":
- unit = value[0]
- default:
- log.Error().Err(errUnexpectedParam)
- }
- }
-
- priceChan := make(chan priceChanStruct, 1)
- errChan := make(chan errorChanStruct, 1)
-
- var waitGroup sync.WaitGroup
-
- ctx, cancel := context.WithTimeout(context.Background(), getTimeout*time.Second)
- defer cancel()
-
- waitGroup.Add(1)
-
- //nolint:contextcheck
- getPriceFromCoinGecko(ctx, name, unit, &waitGroup, priceChan, errChan)
- waitGroup.Wait()
-
- select {
- case err := <-errChan:
- if err.hasError {
- log.Error().Err(err.err)
- }
- default:
- log.Error().Err(errBadLogic).Send()
- }
-
- var price priceChanStruct
- select {
- case priceCh := <-priceChan:
- price = priceCh
- default:
- log.Error().Err(errBadLogic)
- }
-
- responseData := priceResponseData{
- Name: price.name,
- Price: price.price,
- Unit: "USD",
- Err: "",
- IsSuccessful: true,
- }
-
- jsonResp, err := json.Marshal(responseData)
- if err != nil {
- cancel()
- //nolint:gocritic
- log.Fatal().Err(err)
- }
-
- _, err = w.Write(jsonResp)
- if err != nil {
- cancel()
- log.Fatal().Err(err)
- }
-}
-
-func coincapHandler(w http.ResponseWriter, r *http.Request) {
- if r.Method != http.MethodGet {
- http.Error(w, "Method is not supported.", http.StatusNotFound)
- }
-
- w.Header().Add("Content-Type", "application/json")
-
- addSecureHeaders(&w)
-
- var name string
-
- params := r.URL.Query()
- for key, value := range params {
- switch key {
- case "name":
- name = value[0]
- default:
- log.Error().Err(errUnexpectedParam).Send()
- }
- }
-
- priceChan := make(chan priceChanStruct, 1)
- errChan := make(chan errorChanStruct, 1)
-
- var waitGroup sync.WaitGroup
-
- waitGroup.Add(1)
-
- ctx, cancel := context.WithTimeout(context.Background(), getTimeout*time.Second)
- defer cancel()
-
- getPriceFromCoinCap(ctx, name, &waitGroup, priceChan, errChan)
- waitGroup.Wait()
-
- select {
- case err := <-errChan:
- if err.hasError {
- log.Error().Err(err.err)
- }
- default:
- log.Error().Err(errBadLogic)
- }
-
- var price priceChanStruct
- select {
- case priceCh := <-priceChan:
- price = priceCh
- default:
- log.Error().Err(errBadLogic)
- }
-
- responseData := priceResponseData{
- Name: price.name,
- Price: price.price,
- Unit: "USD",
- Err: "",
- IsSuccessful: true,
- }
-
- jsonResp, err := json.Marshal(responseData)
- if err != nil {
- cancel()
-
- log.Fatal().Err(err).Send()
- }
-
- _, err = w.Write(jsonResp)
- if err != nil {
- cancel()
- log.Fatal().Err(err)
- }
-}
-
-func setupLogging() {
- zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
-}
-
-func startServer(gracefulWait time.Duration,
- handlers []HTTPHandler,
- serverDeploymentType string, port string,
-) {
- route := mux.NewRouter()
- cfg := &tls.Config{
- MinVersion: tls.VersionTLS13,
- }
-
- srv := &http.Server{
- Addr: "0.0.0.0:" + port,
- WriteTimeout: time.Second * serverTLSWriteTimeout,
- ReadTimeout: time.Second * serverTLSReadTimeout,
- Handler: route,
- TLSConfig: cfg,
- }
-
- for i := range len(handlers) {
- route.HandleFunc(handlers[i].name, handlers[i].function)
- }
-
- go func() {
- var certPath, keyPath string
-
- switch os.Getenv(serverDeploymentType) {
- case "deployment":
- certPath = "/etc/letsencrypt/live/api.terminaldweller.com/fullchain.pem"
- keyPath = "/etc/letsencrypt/live/api.terminaldweller.com/privkey.pem"
- case "test":
- certPath = "/certs/server.cert"
- keyPath = "/certs/server.key"
- default:
- log.Error().Err(errUnknownDeployment).Send()
- }
-
- if err := srv.ListenAndServeTLS(certPath, keyPath); err != nil {
- log.Error().Err(err)
- }
- }()
-
- c := make(chan os.Signal, 1)
-
- signal.Notify(c, os.Interrupt)
- <-c
-
- ctx, cancel := context.WithTimeout(context.Background(), gracefulWait)
- defer cancel()
-
- if err := srv.Shutdown(ctx); err != nil {
- log.Error().Err(err)
- }
-
- log.Info().Msg("gracefully shut down the server")
-}
-
-func main() {
- var gracefulWait time.Duration
-
- var rdb *redis.Client
-
- flag.DurationVar(
- &gracefulWait,
- "gracefulwait",
- time.Second*defaultGracefulShutdown,
- "the duration to wait during the graceful shutdown",
- )
-
- flagPort := flag.String("port", "8009", "determines the port the server will listen on")
- redisDB := flag.Int64("redisdb", 1, "determines the db number")
- redisAddress := flag.String("redisaddress", "redis:6379", "determines the address of the redis instance")
- redisPassword := flag.String("redispassword", "", "determines the password of the redis db")
- flag.Parse()
-
- rdb = redis.NewClient(&redis.Options{
- Addr: *redisAddress,
- Password: *redisPassword,
- DB: int(*redisDB),
- })
- defer rdb.Close()
-
- setupLogging()
-
- handlerFuncs := []HTTPHandler{
- {name: "/crypto/v1/arb/gecko", function: arbHandler},
- {name: "/crypto/v1/arb/coincap", function: coincapHandler},
- }
-
- startServer(gracefulWait, handlerFuncs, serverDeploymentType, *flagPort)
-}
diff --git a/arbiter/go.mod b/arbiter/go.mod
deleted file mode 100644
index a7a87c6..0000000
--- a/arbiter/go.mod
+++ /dev/null
@@ -1,17 +0,0 @@
-module arbiter
-
-go 1.22
-
-require (
- github.com/go-redis/redis/v8 v8.11.5
- github.com/gorilla/mux v1.8.1
- github.com/rs/zerolog v1.31.0
-)
-
-require (
- github.com/cespare/xxhash/v2 v2.2.0 // indirect
- github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
- github.com/mattn/go-colorable v0.1.13 // indirect
- github.com/mattn/go-isatty v0.0.20 // indirect
- golang.org/x/sys v0.15.0 // indirect
-)
diff --git a/arbiter/go.sum b/arbiter/go.sum
deleted file mode 100644
index 85a6437..0000000
--- a/arbiter/go.sum
+++ /dev/null
@@ -1,41 +0,0 @@
-github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
-github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
-github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
-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.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
-github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
-github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
-github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
-github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
-github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
-github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
-github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
-github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
-github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
-github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
-github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
-github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
-github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
-github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
-github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
-github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
-github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
-github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
-github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
-github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
-github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A=
-github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
-golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 h1:DzZ89McO9/gWPsQXS/FVKAlG02ZjaQ6AlZRBimEYOd0=
-golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
-golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
-golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
-golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
-gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
-gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
-gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
diff --git a/arbiter/tests.sh b/arbiter/tests.sh
deleted file mode 100755
index be923f3..0000000
--- a/arbiter/tests.sh
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/usr/bin/env sh
-
-curl -k -X GET "https://localhost:8009/crypto/v1/arb/gecko?name=ethereum&unit=usd"
-curl -k -X GET "https://localhost:8009/crypto/v1/arb/coincap?name=ethereum"
diff --git a/docker-compose-test.yaml b/docker-compose-test.yaml
index 6b7367a..49ceab9 100644
--- a/docker-compose-test.yaml
+++ b/docker-compose-test.yaml
@@ -1,31 +1,4 @@
services:
- auth:
- image: auth
- build:
- context: ./auth
- deploy:
- resources:
- limits:
- memory: 256M
- logging:
- driver: "json-file"
- options:
- max-size: "100m"
- networks:
- - authnet
- restart: unless-stopped
- ports:
- - "127.0.0.1:8091:8090"
- depends_on:
- - nginx
- volumes:
- - pb-vault:/auth/pb_data
- cap_drop:
- - ALL
- environment:
- - SERVER_DEPLOYMENT_TYPE=deployment
- entrypoint: ["/auth/auth"]
- command: ["serve", "--http=0.0.0.0:8090"]
nginx:
image: nginx:stable
deploy:
@@ -37,9 +10,9 @@ services:
options:
max-size: "100m"
ports:
- - "127.0.0.1:8090:443"
+ - "127.0.0.1:10008:443"
networks:
- - authnet
+ - apinet
restart: unless-stopped
cap_drop:
- ALL
@@ -50,9 +23,11 @@ services:
- SETUID
- NET_BIND_SERVICE
volumes:
- - ./auth/nginx.conf:/etc/nginx/nginx.conf:ro
+ - ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./ss_certs/server.cert:/etc/letsencrypt/live/api.terminaldweller.com/fullchain.pem:ro
- ./ss_certs/server.key:/etc/letsencrypt/live/api.terminaldweller.com/privkey.pem:ro
+ depends_on:
+ - hived
hived:
image: hived
build:
@@ -70,66 +45,23 @@ services:
networks:
- apinet
- dbnet
- - telenet
ports:
- - "127.0.0.1:10008:8008"
+ - "127.0.0.1:10009:8090"
+ entrypoint: ["/hived/hived"]
+ command: ["serve", "--http=0.0.0.0:8090"]
depends_on:
- keydb
- - telebot
- entrypoint: ["/hived/docker-entrypoint.sh"]
cap_drop:
- ALL
environment:
- SERVER_DEPLOYMENT_TYPE=test
- - HIVED_PRICE_SOURCE=cmc
+ - HIVED_PRICE_SOURCE=cryptocompare
- CMC_API_KEY=
- POLYGON_API_KEY=
- telebot:
- image: telebot
- build:
- context: ./telebot
- deploy:
- resources:
- limits:
- memory: 256M
- logging:
- driver: "json-file"
- options:
- max-size: "100m"
- secrets:
- - tg_bot_token
- networks:
- - telenet
- ports:
- - "127.0.0.1:10009:8000"
- entrypoint: ["/telebot/docker-entrypoint.sh"]
- cap_drop:
- - ALL
- environment:
- - SERVER_DEPLOYMENT_TYPE=test
- arbiter:
- image: arbiter
- build:
- context: ./arbiter
- deploy:
- resources:
- limits:
- memory: 256M
- logging:
- driver: "json-file"
- options:
- max-size: "100m"
- networks:
- - apinet
- - dbnet
- - telenet
- ports:
- - "127.0.0.1:8009:8009"
- entrypoint: ["/arbiter/arbiter"]
- cap_drop:
- - ALL
- environment:
- - SERVER_DEPLOYMENT_TYPE=test
+ - CRYPTOCOMPARE_API_KEY=
+ - TELEGRAM_BOT_TOKEN=
+ volumes:
+ - ./hived/hived.toml:/hived/hived.toml
keydb:
image: eqalpha/keydb:alpine_x86_64_v6.3.4
deploy:
@@ -146,12 +78,10 @@ services:
- "127.0.0.1:6380:6379"
environment:
- ALLOW_EMPTY_PASSWORD=yes
- # volumes:
- # - keydb-data:/data/
+ volumes:
+ - keydb-data:/data/
networks:
- authnet:
dbnet:
- telenet:
apinet:
secrets:
tg_bot_token:
diff --git a/docker-compose.yaml b/docker-compose.yaml
index 23e7d32..65d853a 100644
--- a/docker-compose.yaml
+++ b/docker-compose.yaml
@@ -1,80 +1,95 @@
services:
+ nginx:
+ image: nginx:stable
+ deploy:
+ resources:
+ limits:
+ memory: 128M
+ logging:
+ driver: "json-file"
+ options:
+ max-size: "100m"
+ ports:
+ - "127.0.0.1:10008:443"
+ networks:
+ - apinet
+ restart: unless-stopped
+ cap_drop:
+ - ALL
+ cap_add:
+ - CHOWN
+ - DAC_OVERRIDE
+ - SETGID
+ - SETUID
+ - NET_BIND_SERVICE
+ volumes:
+ - ./nginx.conf:/etc/nginx/nginx.conf:ro
+ - /etc/letsencrypt/live/api.terminaldweller.com/fullchain.pem:/etc/letsencrypt/live/api.terminaldweller.com/fullchain.pem:ro
+ - /etc/letsencrypt/live/api.terminaldweller.com/privkey.pem:/etc/letsencrypt/live/api.terminaldweller.com/privkey.pem:ro
+ depends_on:
+ - hived
hived:
image: hived
build:
context: ./hived
+ deploy:
+ resources:
+ limits:
+ memory: 256M
+ logging:
+ driver: "json-file"
+ options:
+ max-size: "100m"
secrets:
- tg_bot_token
networks:
- - mainnet
- - telenet
- restart: unless-stopped
+ - apinet
+ - dbnet
ports:
- - "8007:8008"
+ - "127.0.0.1:10009:8090"
+ entrypoint: ["/hived/hived"]
+ command: ["serve", "--http=0.0.0.0:8090"]
depends_on:
- keydb
- - telebot
- entrypoint: ["/hived/docker-entrypoint.sh"]
- volumes:
- - /etc/letsencrypt/live/api.terminaldweller.com/fullchain.pem:/etc/letsencrypt/live/api.terminaldweller.com/fullchain.pem
- - /etc/letsencrypt/live/api.terminaldweller.com/privkey.pem:/etc/letsencrypt/live/api.terminaldweller.com/privkey.pem
cap_drop:
- ALL
environment:
- - SERVER_DEPLOYMENT_TYPE=deployment
+ - SERVER_DEPLOYMENT_TYPE=test
+ - HIVED_PRICE_SOURCE=cryptocompare
+ - CMC_API_KEY=
- POLYGON_API_KEY=
- CRYPTOCOMPARE_API_KEY=
- - HIVED_PRICE_SOURCE=
- telebot:
- image: telebot
- build:
- context: ./telebot
- secrets:
- - tg_bot_token
- networks:
- - telenet
- restart: unless-stopped
- ports:
- - "127.0.0.1:9009:8000"
- entrypoint: ["/telebot/docker-entrypoint.sh"]
- cap_drop:
- - ALL
- environment:
- - SERVER_DEPLOYMENT_TYPE=deployment
+ - TELEGRAM_BOT_TOKEN=
+ volumes:
+ - ./hived/hived.toml:/hived/hived.toml
keydb:
image: eqalpha/keydb:alpine_x86_64_v6.3.4
+ deploy:
+ resources:
+ limits:
+ memory: 256M
+ logging:
+ driver: "json-file"
+ options:
+ max-size: "100m"
networks:
- - mainnet
- restart: unless-stopped
+ - dbnet
ports:
- - "127.0.0.1:6379:6379"
+ - "127.0.0.1:6380:6379"
environment:
- ALLOW_EMPTY_PASSWORD=yes
volumes:
- - redis-data:/data/
- arbiter:
- image: arbiter
- build:
- context: ./arbiter
- networks:
- - mainnet
- ports:
- - "8009:8009"
- entrypoint: ["/arbiter/arbiter"]
- volumes:
- - /etc/letsencrypt/live/api.terminaldweller.com/fullchain.pem:/etc/letsencrypt/live/api.terminaldweller.com/fullchain.pem
- - /etc/letsencrypt/live/api.terminaldweller.com/privkey.pem:/etc/letsencrypt/live/api.terminaldweller.com/privkey.pem
- cap_drop:
- - ALL
- environment:
- - SERVER_DEPLOYMENT_TYPE=deployment
+ - keydb-data:/data/
networks:
- mainnet:
- driver: bridge
- telenet:
- driver: bridge
+ dbnet:
+ apinet:
secrets:
tg_bot_token:
file: ./tgtoken
+ polygon_api_key:
+ file: ./polygon_api_key
+ cmc_api_key:
+ file: ./cmc_api_key
volumes:
- redis-data:
+ keydb-data:
+ p
diff --git a/hived/Dockerfile b/hived/Dockerfile
index 64e4789..a9c84ac 100644
--- a/hived/Dockerfile
+++ b/hived/Dockerfile
@@ -1,4 +1,4 @@
-FROM golang:1.22-alpine3.19 as builder
+FROM golang:1.22-alpine3.20 as builder
RUN apk update && apk upgrade
RUN apk add go git
COPY go.* /hived/
@@ -6,14 +6,6 @@ RUN cd /hived && go mod download
COPY *.go /hived/
RUN cd /hived && go build
-FROM alpine:3.19 as certbuilder
-RUN apk add openssl
-WORKDIR /certs
-RUN openssl req -nodes -new -x509 -subj="/C=US/ST=Denial/L=springfield/O=Dis/CN=localhost" -keyout server.key -out server.cert
-
-# FROM gcr.io/distroless/static-debian10
-FROM alpine:3.19
-COPY --from=certbuilder /certs /certs
+FROM alpine:3.20
COPY --from=builder /hived/hived /hived/
-COPY ./docker-entrypoint.sh /hived/
-ENTRYPOINT ["/hived/docker-entrypoint.sh"]
+ENTRYPOINT ["/hived/hived"]
diff --git a/hived/alertHandlers.go b/hived/alertHandlers.go
index fd49835..6fb7614 100644
--- a/hived/alertHandlers.go
+++ b/hived/alertHandlers.go
@@ -6,6 +6,7 @@ import (
"net/http"
"time"
+ "github.com/labstack/echo/v5"
"github.com/rs/zerolog/log"
)
@@ -166,7 +167,10 @@ func (alertHandler Handler) HandleAlertGet(writer http.ResponseWriter, request *
}
}
-func alertHandler(writer http.ResponseWriter, request *http.Request) {
+func (aw appWrapper) alertHandler(echoCtx echo.Context) error {
+ writer := echoCtx.Response().Writer
+ request := echoCtx.Request()
+
addSecureHeaders(&writer)
handler := Handler{rdb: rdb}
@@ -185,4 +189,6 @@ func alertHandler(writer http.ResponseWriter, request *http.Request) {
default:
http.Error(writer, "Method is not supported.", http.StatusNotFound)
}
+
+ return nil
}
diff --git a/hived/docker-entrypoint.sh b/hived/docker-entrypoint.sh
deleted file mode 100755
index 0e41826..0000000
--- a/hived/docker-entrypoint.sh
+++ /dev/null
@@ -1,6 +0,0 @@
-#!/bin/sh
-set -ex
-
-export "$(cat /run/secrets/tg_bot_token)"
-
-"/hived/hived" "$@"
diff --git a/hived/go.mod b/hived/go.mod
index dead3e3..d9b8a4e 100644
--- a/hived/go.mod
+++ b/hived/go.mod
@@ -3,33 +3,97 @@ module hived
go 1.22
require (
+ github.com/BurntSushi/toml v0.3.1
github.com/Knetic/govaluate v3.0.0+incompatible
github.com/go-redis/redis/v8 v8.11.5
- github.com/gorilla/mux v1.8.1
- github.com/polygon-io/client-go v1.16.4
+ github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible
+ github.com/labstack/echo/v5 v5.0.0-20230722203903-ec5b858dab61
+ github.com/pocketbase/pocketbase v0.22.13
github.com/rs/zerolog v1.31.0
github.com/terminaldweller/grpc v1.0.3
- google.golang.org/grpc v1.59.0
- google.golang.org/protobuf v1.31.0
+ google.golang.org/grpc v1.64.0
+ google.golang.org/protobuf v1.34.1
)
require (
+ github.com/AlecAivazis/survey/v2 v2.3.7 // indirect
+ github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
+ github.com/aws/aws-sdk-go-v2 v1.27.0 // indirect
+ github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.2 // indirect
+ github.com/aws/aws-sdk-go-v2/config v1.27.16 // indirect
+ github.com/aws/aws-sdk-go-v2/credentials v1.17.16 // indirect
+ github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.3 // indirect
+ github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.16.21 // indirect
+ github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.7 // indirect
+ github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.7 // indirect
+ github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect
+ github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.7 // indirect
+ github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2 // indirect
+ github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.9 // indirect
+ github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.9 // indirect
+ github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.7 // indirect
+ github.com/aws/aws-sdk-go-v2/service/s3 v1.54.3 // indirect
+ github.com/aws/aws-sdk-go-v2/service/sso v1.20.9 // indirect
+ github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.3 // indirect
+ github.com/aws/aws-sdk-go-v2/service/sts v1.28.10 // indirect
+ github.com/aws/smithy-go v1.20.2 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
- github.com/gabriel-vasile/mimetype v1.4.2 // indirect
- github.com/go-playground/form/v4 v4.2.1 // indirect
- github.com/go-playground/locales v0.14.1 // indirect
- github.com/go-playground/universal-translator v0.18.1 // indirect
- github.com/go-playground/validator/v10 v10.16.0 // indirect
- github.com/go-resty/resty/v2 v2.11.0 // indirect
- github.com/golang/protobuf v1.5.3 // indirect
- github.com/google/go-cmp v0.6.0 // indirect
- github.com/leodido/go-urn v1.2.4 // indirect
+ github.com/disintegration/imaging v1.6.2 // indirect
+ github.com/dlclark/regexp2 v1.10.0 // indirect
+ github.com/domodwyer/mailyak/v3 v3.6.2 // indirect
+ github.com/dop251/goja v0.0.0-20231027120936-b396bb4c349d // indirect
+ github.com/dop251/goja_nodejs v0.0.0-20231122114759-e84d9a924c5c // indirect
+ github.com/dustin/go-humanize v1.0.1 // indirect
+ github.com/fatih/color v1.17.0 // indirect
+ github.com/fsnotify/fsnotify v1.7.0 // indirect
+ github.com/gabriel-vasile/mimetype v1.4.4 // indirect
+ github.com/ganigeorgiev/fexpr v0.4.1 // indirect
+ github.com/go-ozzo/ozzo-validation/v4 v4.3.0 // indirect
+ github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect
+ github.com/goccy/go-json v0.10.3 // indirect
+ github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
+ github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
+ github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd // indirect
+ github.com/google/uuid v1.6.0 // indirect
+ github.com/googleapis/gax-go/v2 v2.12.4 // indirect
+ github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
+ github.com/inconshreveable/mousetrap v1.1.0 // indirect
+ github.com/jmespath/go-jmespath v0.4.0 // indirect
+ github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
- golang.org/x/crypto v0.17.0 // indirect
- golang.org/x/net v0.19.0 // indirect
- golang.org/x/sys v0.15.0 // indirect
- golang.org/x/text v0.14.0 // indirect
- google.golang.org/genproto/googleapis/rpc v0.0.0-20231127180814-3a041ad873d4 // indirect
+ github.com/mattn/go-sqlite3 v1.14.22 // indirect
+ github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
+ github.com/ncruces/go-strftime v0.1.9 // indirect
+ github.com/pocketbase/dbx v1.10.1 // indirect
+ github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
+ github.com/spf13/cast v1.6.0 // indirect
+ github.com/spf13/cobra v1.8.0 // indirect
+ github.com/spf13/pflag v1.0.5 // indirect
+ github.com/stretchr/testify v1.8.4 // indirect
+ github.com/technoweenie/multipartstreamer v1.0.1 // indirect
+ github.com/valyala/bytebufferpool v1.0.0 // indirect
+ github.com/valyala/fasttemplate v1.2.2 // indirect
+ go.opencensus.io v0.24.0 // indirect
+ gocloud.dev v0.37.0 // indirect
+ golang.org/x/crypto v0.23.0 // indirect
+ golang.org/x/image v0.16.0 // indirect
+ golang.org/x/net v0.25.0 // indirect
+ golang.org/x/oauth2 v0.20.0 // indirect
+ golang.org/x/sync v0.7.0 // indirect
+ golang.org/x/sys v0.20.0 // indirect
+ golang.org/x/term v0.20.0 // indirect
+ golang.org/x/text v0.15.0 // indirect
+ golang.org/x/time v0.5.0 // indirect
+ golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
+ google.golang.org/api v0.182.0 // indirect
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 // indirect
+ modernc.org/gc/v3 v3.0.0-20240304020402-f0dba7c97c2b // indirect
+ modernc.org/libc v1.51.0 // indirect
+ modernc.org/mathutil v1.6.0 // indirect
+ modernc.org/memory v1.8.0 // indirect
+ modernc.org/sqlite v1.30.0 // indirect
+ modernc.org/strutil v1.2.0 // indirect
+ modernc.org/token v1.1.0 // indirect
)
diff --git a/hived/go.sum b/hived/go.sum
index 2a70c3b..0bc72a3 100644
--- a/hived/go.sum
+++ b/hived/go.sum
@@ -1,13 +1,79 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.114.0 h1:OIPFAdfrFDFO2ve2U7r/H5SwSbBzEdrBdE7xkgwc+kY=
+cloud.google.com/go v0.114.0/go.mod h1:ZV9La5YYxctro1HTPug5lXH/GefROyW8PPD4T8n9J8E=
+cloud.google.com/go/auth v0.4.2 h1:sb0eyLkhRtpq5jA+a8KWw0W70YcdVca7KJ8TM0AFYDg=
+cloud.google.com/go/auth v0.4.2/go.mod h1:Kqvlz1cf1sNA0D+sYJnkPQOP+JMHkuHeIgVmCRtZOLc=
+cloud.google.com/go/auth/oauth2adapt v0.2.2 h1:+TTV8aXpjeChS9M+aTtN/TjdQnzJvmzKFt//oWu7HX4=
+cloud.google.com/go/auth/oauth2adapt v0.2.2/go.mod h1:wcYjgpZI9+Yu7LyYBg4pqSiaRkfEK3GQcpb7C/uyF1Q=
+cloud.google.com/go/compute v1.25.1 h1:ZRpHJedLtTpKgr3RV1Fx23NuaAEN1Zfx9hw1u4aJdjU=
+cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc=
+cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
+cloud.google.com/go/iam v1.1.6 h1:bEa06k05IO4f4uJonbB5iAgKTPpABy1ayxaIZV/GHVc=
+cloud.google.com/go/iam v1.1.6/go.mod h1:O0zxdPeGBoFdWW3HWmBxJsk0pfvNM/p/qa82rWOGTwI=
+cloud.google.com/go/storage v1.39.1 h1:MvraqHKhogCOTXTlct/9C3K3+Uy2jBmFYb3/Sp6dVtY=
+cloud.google.com/go/storage v1.39.1/go.mod h1:xK6xZmxZmo+fyP7+DEF6FhNc24/JAe95OLyOHCXFH1o=
+filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
+filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
+github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ=
+github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo=
+github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
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/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s=
+github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
+github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg=
+github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
+github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
+github.com/aws/aws-sdk-go v1.51.11 h1:El5VypsMIz7sFwAAj/j06JX9UGs4KAbAIEaZ57bNY4s=
+github.com/aws/aws-sdk-go v1.51.11/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
+github.com/aws/aws-sdk-go-v2 v1.27.0 h1:7bZWKoXhzI+mMR/HjdMx8ZCC5+6fY0lS5tr0bbgiLlo=
+github.com/aws/aws-sdk-go-v2 v1.27.0/go.mod h1:ffIFB97e2yNsv4aTSGkqtHnppsIJzw7G7BReUZ3jCXM=
+github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.2 h1:x6xsQXGSmW6frevwDA+vi/wqhp1ct18mVXYN08/93to=
+github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.2/go.mod h1:lPprDr1e6cJdyYeGXnRaJoP4Md+cDBvi2eOj00BlGmg=
+github.com/aws/aws-sdk-go-v2/config v1.27.16 h1:knpCuH7laFVGYTNd99Ns5t+8PuRjDn4HnnZK48csipM=
+github.com/aws/aws-sdk-go-v2/config v1.27.16/go.mod h1:vutqgRhDUktwSge3hrC3nkuirzkJ4E/mLj5GvI0BQas=
+github.com/aws/aws-sdk-go-v2/credentials v1.17.16 h1:7d2QxY83uYl0l58ceyiSpxg9bSbStqBC6BeEeHEchwo=
+github.com/aws/aws-sdk-go-v2/credentials v1.17.16/go.mod h1:Ae6li/6Yc6eMzysRL2BXlPYvnrLLBg3D11/AmOjw50k=
+github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.3 h1:dQLK4TjtnlRGb0czOht2CevZ5l6RSyRWAnKeGd7VAFE=
+github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.3/go.mod h1:TL79f2P6+8Q7dTsILpiVST+AL9lkF6PPGI167Ny0Cjw=
+github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.16.21 h1:1v8Ii0MRVGYB/sdhkbxrtolCA7Tp+lGh+5OJTs5vmZ8=
+github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.16.21/go.mod h1:cxdd1rc8yxCjKz28hi30XN1jDXr2DxZvD44vLxTz/bg=
+github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.7 h1:lf/8VTF2cM+N4SLzaYJERKEWAXq8MOMpZfU6wEPWsPk=
+github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.7/go.mod h1:4SjkU7QiqK2M9oozyMzfZ/23LmUY+h3oFqhdeP5OMiI=
+github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.7 h1:4OYVp0705xu8yjdyoWix0r9wPIRXnIzzOoUpQVHIJ/g=
+github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.7/go.mod h1:vd7ESTEvI76T2Na050gODNmNU7+OyKrIKroYTu4ABiI=
+github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU=
+github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY=
+github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.7 h1:/FUtT3xsoHO3cfh+I/kCbcMCN98QZRsiFet/V8QkWSs=
+github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.7/go.mod h1:MaCAgWpGooQoCWZnMur97rGn5dp350w2+CeiV5406wE=
+github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2 h1:Ji0DY1xUsUr3I8cHps0G+XM3WWU16lP6yG8qu1GAZAs=
+github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2/go.mod h1:5CsjAbs3NlGQyZNFACh+zztPDI7fU6eW9QsxjfnuBKg=
+github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.9 h1:UXqEWQI0n+q0QixzU0yUUQBZXRd5037qdInTIHFTl98=
+github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.9/go.mod h1:xP6Gq6fzGZT8w/ZN+XvGMZ2RU1LeEs7b2yUP5DN8NY4=
+github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.9 h1:Wx0rlZoEJR7JwlSZcHnEa7CNjrSIyVxMFWGAaXy4fJY=
+github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.9/go.mod h1:aVMHdE0aHO3v+f/iw01fmXV/5DbfQ3Bi9nN7nd9bE9Y=
+github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.7 h1:uO5XR6QGBcmPyo2gxofYJLFkcVQ4izOoGDNenlZhTEk=
+github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.7/go.mod h1:feeeAYfAcwTReM6vbwjEyDmiGho+YgBhaFULuXDW8kc=
+github.com/aws/aws-sdk-go-v2/service/s3 v1.54.3 h1:57NtjG+WLims0TxIQbjTqebZUKDM03DfM11ANAekW0s=
+github.com/aws/aws-sdk-go-v2/service/s3 v1.54.3/go.mod h1:739CllldowZiPPsDFcJHNF4FXrVxaSGVnZ9Ez9Iz9hc=
+github.com/aws/aws-sdk-go-v2/service/sso v1.20.9 h1:aD7AGQhvPuAxlSUfo0CWU7s6FpkbyykMhGYMvlqTjVs=
+github.com/aws/aws-sdk-go-v2/service/sso v1.20.9/go.mod h1:c1qtZUWtygI6ZdvKppzCSXsDOq5I4luJPZ0Ud3juFCA=
+github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.3 h1:Pav5q3cA260Zqez42T9UhIlsd9QeypszRPwC9LdSSsQ=
+github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.3/go.mod h1:9lmoVDVLz/yUZwLaQ676TK02fhCu4+PgRSmMaKR1ozk=
+github.com/aws/aws-sdk-go-v2/service/sts v1.28.10 h1:69tpbPED7jKPyzMcrwSvhWcJ9bPnZsZs18NT40JwM0g=
+github.com/aws/aws-sdk-go-v2/service/sts v1.28.10/go.mod h1:0Aqn1MnEuitqfsCNyKsdKLhDUOr4txD/g19EfiUqgws=
+github.com/aws/smithy-go v1.20.2 h1:tbp628ireGtzcHDDmLT/6ADHidqnwgF57XOXZe6tp4Q=
+github.com/aws/smithy-go v1.20.2/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY=
+github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic=
+github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
@@ -16,41 +82,78 @@ github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWH
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
+github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
+github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
+github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI=
+github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/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/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
+github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
+github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
+github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
+github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0=
+github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
+github.com/domodwyer/mailyak/v3 v3.6.2 h1:x3tGMsyFhTCaxp6ycgR0FE/bu5QiNp+hetUuCOBXMn8=
+github.com/domodwyer/mailyak/v3 v3.6.2/go.mod h1:lOm/u9CyCVWHeaAmHIdF4RiKVxKUT/H5XX10lIKAL6c=
+github.com/dop251/goja v0.0.0-20211022113120-dc8c55024d06/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk=
+github.com/dop251/goja v0.0.0-20231027120936-b396bb4c349d h1:wi6jN5LVt/ljaBG4ue79Ekzb12QfJ52L9Q98tl8SWhw=
+github.com/dop251/goja v0.0.0-20231027120936-b396bb4c349d/go.mod h1:QMWlm50DNe14hD7t24KEqZuUdC9sOTy8W6XbCU1mlw4=
+github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y=
+github.com/dop251/goja_nodejs v0.0.0-20211022123610-8dd9abb0616d/go.mod h1:DngW8aVqWbuLRMHItjPUyqdj+HWPvnQe8V8y1nDpIbM=
+github.com/dop251/goja_nodejs v0.0.0-20231122114759-e84d9a924c5c h1:hLoodLRD4KLWIH8eyAQCLcH8EqIrjac7fCkp/fHnvuQ=
+github.com/dop251/goja_nodejs v0.0.0-20231122114759-e84d9a924c5c/go.mod h1:bhGPmCgCCTSRfiMYWjpS46IDo9EUZXlsuUaPXSWGbv0=
+github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
+github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
-github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
-github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
-github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
-github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
+github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4=
+github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI=
+github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
+github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
+github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
+github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
+github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
+github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
+github.com/gabriel-vasile/mimetype v1.4.4 h1:QjV6pZ7/XZ7ryI2KuyeEDE8wnh7fHP9YnQy+R0LnH8I=
+github.com/gabriel-vasile/mimetype v1.4.4/go.mod h1:JwLei5XPtWdGiMFB5Pjle1oEeoSeEuJfJE+TtfvdB/s=
+github.com/ganigeorgiev/fexpr v0.4.1 h1:hpUgbUEEWIZhSDBtf4M9aUNfQQ0BZkGRaMePy7Gcx5k=
+github.com/ganigeorgiev/fexpr v0.4.1/go.mod h1:RyGiGqmeXhEQ6+mlGdnUleLHgtzzu/VGO2WtJkF5drE=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
-github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
-github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
-github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
-github.com/go-playground/form/v4 v4.2.1 h1:HjdRDKO0fftVMU5epjPW2SOREcZ6/wLUzEobqUGJuPw=
-github.com/go-playground/form/v4 v4.2.1/go.mod h1:q1a2BY+AQUUzhl6xA/6hBetay6dEIhMHjgvJiGo6K7U=
-github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
-github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
-github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
-github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
-github.com/go-playground/validator/v10 v10.16.0 h1:x+plE831WK4vaKHO/jpgUGsvLKIqRRkz6M78GuJAfGE=
-github.com/go-playground/validator/v10 v10.16.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
+github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
+github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
+github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
+github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
+github.com/go-ozzo/ozzo-validation/v4 v4.3.0 h1:byhDUpfEwjsVQb1vBunvIjh2BHQ9ead57VkAEY4V+Es=
+github.com/go-ozzo/ozzo-validation/v4 v4.3.0/go.mod h1:2NKgrcHl3z6cJs+3Oo940FPRiTzuqKbvfrL2RxCj6Ew=
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
-github.com/go-resty/resty/v2 v2.11.0 h1:i7jMfNOJYMp69lq7qozJP+bjgzfAzeOhuGlyDrqxT/8=
-github.com/go-resty/resty/v2 v2.11.0/go.mod h1:iiP/OpA0CkcL3IGt1O0+/SIItFUbkkyw5BGXiVdTu+A=
+github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU=
+github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
+github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
+github.com/go-sql-driver/mysql v1.8.0 h1:UtktXaU2Nb64z/pLiGIxY4431SJ4/dR5cjMmlVHgnT4=
+github.com/go-sql-driver/mysql v1.8.0/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
+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/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
+github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
+github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
+github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
+github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
@@ -63,30 +166,71 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
-github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
-github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
+github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
+github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
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.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8qtYCYyzA+8c/9qtqgA3qsXGYqCPKARAFg=
+github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo=
+github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
+github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o=
+github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
-github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/wire v0.6.0 h1:HBkoIh4BdSxoyo9PveV8giw7ZsaBOvzWKfcg/6MrVwI=
+github.com/google/wire v0.6.0/go.mod h1:F4QhpQ9EDIdJ1Mbop/NZBRB+5yrR6qg3BnctaoUk6NA=
+github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs=
+github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0=
+github.com/googleapis/gax-go/v2 v2.12.4 h1:9gWcmF85Wvq4ryPFvGFaOgPIs1AQX0d0bcbGw4Z96qg=
+github.com/googleapis/gax-go/v2 v2.12.4/go.mod h1:KYEYLorsnIGDi/rPC8b5TdlB9kbKoFubselGIoBMCwI=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
-github.com/jarcoal/httpmock v1.3.1 h1:iUx3whfZWVf3jT01hQTO/Eo5sAYtB2/rqaUuOtpInww=
-github.com/jarcoal/httpmock v1.3.1/go.mod h1:3yb8rc4BI7TCBhFY8ng0gjuLKJNquuDNiPaZjnENuYg=
-github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
-github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
+github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
+github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
+github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog=
+github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68=
+github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
+github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
+github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
+github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
+github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
+github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
+github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
+github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
+github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
+github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
+github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/labstack/echo/v5 v5.0.0-20230722203903-ec5b858dab61 h1:FwuzbVh87iLiUQj1+uQUsuw9x5t9m5n5g7rG7o4svW4=
+github.com/labstack/echo/v5 v5.0.0-20230722203903-ec5b858dab61/go.mod h1:paQfF1YtHe+GrGg5fOgjsjoCX/UKDr9bc1DoWpZfns8=
+github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
+github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
+github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
+github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
+github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
+github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
+github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
+github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
@@ -96,93 +240,134 @@ github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5h
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/polygon-io/client-go v1.16.4 h1:3hxFXAVOtbQ9EfzyI65f5qNMWYAAJ/SoBktk4al0YAY=
-github.com/polygon-io/client-go v1.16.4/go.mod h1:jkG484PfMaqiubeFdoT38nrMOx1zclJcjccxDc/BEsE=
+github.com/pocketbase/dbx v1.10.1 h1:cw+vsyfCJD8YObOVeqb93YErnlxwYMkNZ4rwN0G0AaA=
+github.com/pocketbase/dbx v1.10.1/go.mod h1:xXRCIAKTHMgUCyCKZm55pUOdvFziJjQfXaWKhu2vhMs=
+github.com/pocketbase/pocketbase v0.22.13 h1:6703U9aByDueyVCuASqTfvMUrH2Beq26WiohOXp/vk4=
+github.com/pocketbase/pocketbase v0.22.13/go.mod h1:QrZElN3ifiRf9aCHv9ks6JGWYdY3nLO6uuf5/0cLXiI=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
+github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
+github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
+github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
+github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A=
github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
+github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
+github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
+github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
+github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
+github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
+github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
-github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
+github.com/technoweenie/multipartstreamer v1.0.1 h1:XRztA5MXiR1TIRHxH2uNxXxaIkKQDeX7m2XsSOlQEnM=
+github.com/technoweenie/multipartstreamer v1.0.1/go.mod h1:jNVxdtShOxzAsukZwTSw6MDx5eUJoiEBsSvzDU9uzog=
github.com/terminaldweller/grpc v1.0.3 h1:yCRm0HKRD4M87CBmmO5KjkSFRq4X2lzHLJRH1ApzeiE=
github.com/terminaldweller/grpc v1.0.3/go.mod h1:pYpuXZw8rHZwTABVEVZEfErFr+PyEhAaSrFm7y1yvTo=
+github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
+github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
+github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
+github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
+go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
+go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
+go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 h1:4Pp6oUg3+e/6M4C0A/3kJ2VYa++dsWVTtGgLVj5xtHg=
+go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0=
+go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk=
+go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw=
+go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo=
+go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=
+go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI=
+go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco=
+go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
+go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
+gocloud.dev v0.37.0 h1:XF1rN6R0qZI/9DYjN16Uy0durAmSlf58DHOcb28GPro=
+gocloud.dev v0.37.0/go.mod h1:7/O4kqdInCNsc6LqgmuFnS0GRew4XNNYWpA44yQnwco=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
-golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
-golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
+golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
+golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
+golang.org/x/image v0.16.0 h1:9kloLAKhUufZhA12l5fwnx2NZW39/we1UhBesW433jw=
+golang.org/x/image v0.16.0/go.mod h1:ugSZItdV4nOxyqp56HmXwH0Ry0nBCpjnZdpDaIHdoPs=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
-golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
+golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic=
+golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
-golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
-golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
-golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
-golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
+golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
+golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo=
+golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/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-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
+golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/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-20190222072716-a9d3bda3a223/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-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-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
-golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
+golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
-golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
-golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
-golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
+golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
+golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
-golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
-golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
-golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
-golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
-golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
-golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
-golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
+golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
+golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
+golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
@@ -190,27 +375,38 @@ golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
-golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
+golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw=
+golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/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=
+golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU=
+golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
+google.golang.org/api v0.182.0 h1:if5fPvudRQ78GeRx3RayIoiuV7modtErPIZC/T2bIvE=
+google.golang.org/api v0.182.0/go.mod h1:cGhjy4caqA5yXRzEhkHI8Y9mfyC2VLTlER2l08xaqtM=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20231127180814-3a041ad873d4 h1:DC7wcm+i+P1rN3Ff07vL+OndGg5OhNddHyTA+ocPqYE=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20231127180814-3a041ad873d4/go.mod h1:eJVxU6o+4G1PSczBr85xmyvSNYAKvAYgkub40YGomFM=
+google.golang.org/genproto v0.0.0-20240311173647-c811ad7063a7 h1:ImUcDPHjTrAqNhlOkSocDLfG9rrNHH7w7uoKWPaWZ8s=
+google.golang.org/genproto v0.0.0-20240311173647-c811ad7063a7/go.mod h1:/3XmxOjePkvmKrHuBy4zNFw7IzxJXtAgdpXi8Ll990U=
+google.golang.org/genproto/googleapis/api v0.0.0-20240415180920-8c6c420018be h1:Zz7rLWqp0ApfsR/l7+zSHhY3PMiH2xqgxlfYfAfNpoU=
+google.golang.org/genproto/googleapis/api v0.0.0-20240415180920-8c6c420018be/go.mod h1:dvdCTIoAGbkWbcIKBniID56/7XHTt6WfxXNMxuziJ+w=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 h1:Zy9XzmMEflZ/MAaA7vNcoebnRAld7FsPW1EeBB7V0m8=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
+google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
-google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk=
-google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98=
+google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY=
+google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg=
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=
@@ -223,13 +419,17 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
-google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
-google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
+google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
+gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
@@ -237,3 +437,29 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+modernc.org/cc/v4 v4.21.2 h1:dycHFB/jDc3IyacKipCNSDrjIC0Lm1hyoWOZTRR20Lk=
+modernc.org/cc/v4 v4.21.2/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ=
+modernc.org/ccgo/v4 v4.17.8 h1:yyWBf2ipA0Y9GGz/MmCmi3EFpKgeS7ICrAFes+suEbs=
+modernc.org/ccgo/v4 v4.17.8/go.mod h1:buJnJ6Fn0tyAdP/dqePbrrvLyr6qslFfTbFrCuaYvtA=
+modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
+modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ=
+modernc.org/gc/v2 v2.4.1 h1:9cNzOqPyMJBvrUipmynX0ZohMhcxPtMccYgGOJdOiBw=
+modernc.org/gc/v2 v2.4.1/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU=
+modernc.org/gc/v3 v3.0.0-20240304020402-f0dba7c97c2b h1:BnN1t+pb1cy61zbvSUV7SeI0PwosMhlAEi/vBY4qxp8=
+modernc.org/gc/v3 v3.0.0-20240304020402-f0dba7c97c2b/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4=
+modernc.org/libc v1.51.0 h1:kjSHjz1guHbI5iRdi6nEr/wIKSN6X4vzLd6TJMN+lHA=
+modernc.org/libc v1.51.0/go.mod h1:15P6ublJ9FJR8YQCGy8DeQ2Uwur7iW9Hserr/T3OFZE=
+modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
+modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
+modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E=
+modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU=
+modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
+modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
+modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc=
+modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss=
+modernc.org/sqlite v1.30.0 h1:8YhPUs/HTnlEgErn/jSYQTwHN/ex8CjHHjg+K9iG7LM=
+modernc.org/sqlite v1.30.0/go.mod h1:cgkTARJ9ugeXSNaLBPK3CqbOe7Ec7ZhWPoMFGldEYEw=
+modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
+modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
+modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
+modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
diff --git a/hived/hived.go b/hived/hived.go
index f2764a4..c714cfd 100644
--- a/hived/hived.go
+++ b/hived/hived.go
@@ -2,35 +2,41 @@ package main
import (
"context"
- "crypto/tls"
"encoding/json"
"errors"
"flag"
"fmt"
+ "io"
"net/http"
"net/url"
"os"
- "os/signal"
+ "path/filepath"
"strconv"
+ "strings"
"sync"
"time"
+ "github.com/BurntSushi/toml"
"github.com/Knetic/govaluate"
"github.com/go-redis/redis/v8"
- "github.com/gorilla/mux"
+ tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api"
+ "github.com/labstack/echo/v5"
+ "github.com/pocketbase/pocketbase"
+ "github.com/pocketbase/pocketbase/apis"
+ "github.com/pocketbase/pocketbase/core"
+ "github.com/pocketbase/pocketbase/plugins/ghupdate"
+ "github.com/pocketbase/pocketbase/plugins/jsvm"
+ "github.com/pocketbase/pocketbase/plugins/migratecmd"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
- pb "github.com/terminaldweller/grpc/telebot/v1"
- "google.golang.org/grpc"
- "google.golang.org/protobuf/types/known/timestamppb"
)
const (
cryptocomparePriceURL = "https://min-api.cryptocompare.com/data/price?"
polygonCryptoTickerURL = "https://api.polygon.io/v2/snapshot/locale/global/markets/crypto/tickers"
cmcCryptoTickerURL = "https://pro-api.coinmarketcap.com/v2/cryptocurrency/quotes/latest"
- telegramBotTokenEnvVar = "TELEGRAM_BOT_TOKEN" //nolint: gosec
- serverDeploymentType = "SERVER_DEPLOYMENT_TYPE"
+ coingeckoAPIURLv3 = "https://api.coingecko.com/api/v3"
+ coincapAPIURLv2 = "https://api.coincap.io/v2"
httpClientTimeout = 5
getTimeout = 5
serverTLSReadTimeout = 15
@@ -61,6 +67,86 @@ var (
errUnknownDeploymentKind = errors.New("unknown deployment kind")
)
+type HivedConfig struct {
+ KeydbAddress string `toml:"keydbAddress"`
+ KeydbPassword string `toml:"keydbPassword"`
+ KeydbDB int `toml:"keydbDB"`
+ AlertsCheckInterval int64 `toml:"alertsCheckInterval"`
+ TickerCheckInterval int64 `toml:"tickerCheckInterval"`
+ CacheDuration int64 `toml:"cacheDuration"`
+ TelegramChannelID int64 `toml:"telegramChannelID"`
+}
+
+type appWrapper struct {
+ app *pocketbase.PocketBase
+}
+
+// https://docs.coincap.io/
+type CoinCapAssetGetResponseData struct {
+ ID string `json:"id"`
+ Rank string `json:"rank"`
+ Symbol string `json:"symbol"`
+ Name string `json:"name"`
+ Supply string `json:"supply"`
+ MaxSupply string `json:"maxSupply"`
+ MarketCapUsd string `json:"marketCapUsd"`
+ VolumeUsd24Hr string `json:"volumeUsd24Hr"`
+ PriceUsd string `json:"priceUsd"`
+ ChangePercent24Hr string `json:"changePercent24Hr"`
+ Vwap24Hr string `json:"vwap24Hr"`
+}
+
+type RootCmds struct {
+ hooksDir string
+ hooksWatch bool
+ hooksPool int
+ hooksPoolSize int
+ migrationsDir string
+ automigrate bool
+ publicDir string
+ indexFallback bool
+ queryTimeout int
+}
+
+type priceResponseData struct {
+ Name string `json:"name"`
+ Price float64 `json:"price"`
+ Unit string `json:"unit"`
+ Err string `json:"err"`
+ IsSuccessful bool `json:"isSuccessful"`
+}
+
+type CoinCapAssetGetResponse struct {
+ Data CoinCapAssetGetResponseData `json:"data"`
+ TimeStamp int64 `json:"timestamp"`
+}
+
+type HTTPHandlerFunc func(http.ResponseWriter, *http.Request)
+
+type HTTPHandler struct {
+ name string
+ function HTTPHandlerFunc
+}
+
+func getTGBot() *tgbotapi.BotAPI {
+ token := os.Getenv("TELEGRAM_BOT_TOKEN")
+ fmt.Println("YYY:", token)
+
+ bot, err := tgbotapi.NewBotAPI(token)
+ if err != nil {
+ log.Fatal().Err(err).Send()
+ }
+
+ return bot
+}
+
+func sendMessage(bot *tgbotapi.BotAPI, msgText string, channelID int64) error {
+ msg := tgbotapi.NewMessage(channelID, msgText)
+ _, err := bot.Send(msg)
+
+ return err
+}
+
func GetProxiedClient() *http.Client {
transport := &http.Transport{
DisableKeepAlives: true,
@@ -86,30 +172,13 @@ func addSecureHeaders(writer *http.ResponseWriter) {
(*writer).Header().Set("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS")
}
-func sendToTg(address, msg string, channelID int64) {
- conn, err := grpc.Dial(address, grpc.WithInsecure())
- if err != nil {
- log.Fatal().Err(err)
- }
- defer conn.Close()
-
- c := pb.NewNotificationServiceClient(conn)
+func sendToTg(msg string, channelID int64) {
+ tgbotapi := getTGBot()
- ctx, cancel := context.WithTimeout(context.Background(), telegramTimeout*time.Second)
- defer cancel()
-
- response, err := c.Notify(
- ctx,
- &pb.NotificationRequest{
- NotificationText: msg,
- ChannelId: channelID,
- RequestTime: timestamppb.Now(),
- })
+ err := sendMessage(tgbotapi, msg, channelID)
if err != nil {
- log.Error().Err(err)
+ log.Info().Err(err)
}
-
- log.Info().Msg(fmt.Sprintf("%v", response))
}
type priceChanStruct struct {
@@ -175,6 +244,8 @@ func getPriceFromCryptoCompare(
"tsyms=" + url.QueryEscape(unit)
path := cryptocomparePriceURL + params
+ log.Print(path)
+
client := GetProxiedClient()
req, err := http.NewRequestWithContext(ctx, http.MethodGet, path, nil)
@@ -326,7 +397,160 @@ func getPriceFromCMC(
errChan <- errorChanStruct{hasError: false, err: nil}
}
-func PriceHandler(writer http.ResponseWriter, request *http.Request) {
+func GetPriceFromCoinGecko(
+ ctx context.Context,
+ name, unit string,
+ wg *sync.WaitGroup,
+ priceChan chan<- priceChanStruct,
+ errChan chan<- errorChanStruct,
+) {
+ defer wg.Done()
+
+ priceFloat := 0.
+
+ params := "/simple/price?ids=" + url.QueryEscape(name) + "&" +
+ "vs_currencies=" + url.QueryEscape(unit)
+ path := coingeckoAPIURLv3 + params
+
+ client := GetProxiedClient()
+
+ req, err := http.NewRequestWithContext(ctx, http.MethodGet, path, nil)
+ if err != nil {
+ priceChan <- priceChanStruct{name: name, price: priceFloat}
+ errChan <- errorChanStruct{hasError: true, err: err}
+
+ log.Error().Err(err)
+
+ return
+ }
+
+ resp, err := client.Do(req)
+ if err != nil {
+ priceChan <- priceChanStruct{name: name, price: priceFloat}
+ errChan <- errorChanStruct{hasError: true, err: err}
+
+ log.Error().Err(err).Send()
+
+ return
+ }
+ defer resp.Body.Close()
+
+ body, err := io.ReadAll(resp.Body)
+ if err != nil {
+ priceChan <- priceChanStruct{name: name, price: priceFloat}
+ errChan <- errorChanStruct{hasError: true, err: err}
+
+ log.Error().Err(err).Send()
+ }
+
+ jsonBody := make(map[string]interface{})
+
+ err = json.Unmarshal(body, &jsonBody)
+ if err != nil {
+ priceChan <- priceChanStruct{name: name, price: priceFloat}
+ errChan <- errorChanStruct{hasError: true, err: err}
+
+ log.Error().Err(err).Send()
+ }
+
+ price, isOk := jsonBody[name].(map[string]interface{})
+ if !isOk {
+ priceChan <- priceChanStruct{name: name, price: priceFloat}
+ errChan <- errorChanStruct{hasError: true, err: err}
+
+ log.Error().Err(err)
+
+ return
+ }
+
+ log.Info().Msg(string(body))
+
+ priceFloat, isOk = price[unit].(float64)
+ if !isOk {
+ priceChan <- priceChanStruct{name: name, price: priceFloat}
+ errChan <- errorChanStruct{hasError: true, err: err}
+
+ log.Error().Err(err)
+
+ return
+ }
+
+ priceChan <- priceChanStruct{name: name, price: priceFloat}
+ errChan <- errorChanStruct{hasError: false, err: nil}
+}
+
+func GetPriceFromCoinCap(
+ ctx context.Context,
+ name string,
+ wg *sync.WaitGroup,
+ priceChan chan<- priceChanStruct,
+ errChan chan<- errorChanStruct,
+) {
+ defer wg.Done()
+
+ priceFloat := 0.
+
+ params := "/assets/" + url.QueryEscape(name)
+ path := coincapAPIURLv2 + params
+
+ client := GetProxiedClient()
+
+ req, err := http.NewRequestWithContext(ctx, http.MethodGet, path, nil)
+ if err != nil {
+ priceChan <- priceChanStruct{name: name, price: priceFloat}
+ errChan <- errorChanStruct{hasError: true, err: err}
+
+ log.Error().Err(err)
+
+ return
+ }
+
+ resp, err := client.Do(req)
+ if err != nil {
+ priceChan <- priceChanStruct{name: name, price: priceFloat}
+ errChan <- errorChanStruct{hasError: true, err: err}
+
+ log.Error().Err(err).Send()
+
+ return
+ }
+ defer resp.Body.Close()
+
+ body, err := io.ReadAll(resp.Body)
+ if err != nil {
+ priceChan <- priceChanStruct{name: name, price: priceFloat}
+ errChan <- errorChanStruct{hasError: true, err: err}
+
+ log.Error().Err(err)
+ }
+
+ var coinCapAssetGetResponse CoinCapAssetGetResponse
+
+ err = json.Unmarshal(body, &coinCapAssetGetResponse)
+ if err != nil {
+ priceChan <- priceChanStruct{name: name, price: priceFloat}
+ errChan <- errorChanStruct{hasError: true, err: err}
+
+ log.Error().Err(err).Send()
+ }
+
+ priceFloat, err = strconv.ParseFloat(coinCapAssetGetResponse.Data.PriceUsd, 64)
+ if err != nil {
+ priceChan <- priceChanStruct{name: name, price: priceFloat}
+ errChan <- errorChanStruct{hasError: true, err: err}
+
+ log.Error().Err(err).Send()
+ }
+
+ log.Info().Msg(string(body))
+
+ priceChan <- priceChanStruct{name: name, price: priceFloat}
+ errChan <- errorChanStruct{hasError: false, err: nil}
+}
+
+func (aw appWrapper) PriceHandler(echoCtx echo.Context) error {
+ writer := echoCtx.Response().Writer
+ request := echoCtx.Request()
writer.Header().Add("Content-Type", "application/json")
if request.Method != http.MethodGet {
@@ -361,7 +585,7 @@ func PriceHandler(writer http.ResponseWriter, request *http.Request) {
http.Error(writer, "internal server error", http.StatusInternalServerError)
}
- return
+ return nil
}
var waitGroup sync.WaitGroup
@@ -407,19 +631,26 @@ func PriceHandler(writer http.ResponseWriter, request *http.Request) {
if err != nil {
log.Error().Err(err)
http.Error(writer, "internal server error", http.StatusInternalServerError)
+
+ return err
}
+
+ return nil
}
-func PairHandler(w http.ResponseWriter, r *http.Request) {
+func (aw appWrapper) PairHandler(echoCtx echo.Context) error {
var err error
- w.Header().Add("Content-Type", "application/json")
+ writer := echoCtx.Response().Writer
+ request := echoCtx.Request()
- if r.Method != http.MethodGet {
- http.Error(w, "Method is not supported.", http.StatusNotFound)
+ writer.Header().Add("Content-Type", "application/json")
+
+ if request.Method != http.MethodGet {
+ http.Error(writer, "Method is not supported.", http.StatusNotFound)
}
- addSecureHeaders(&w)
+ addSecureHeaders(&writer)
var one string
@@ -427,7 +658,7 @@ func PairHandler(w http.ResponseWriter, r *http.Request) {
var multiplier float64
- params := r.URL.Query()
+ params := request.URL.Query()
for key, value := range params {
switch key {
case "one":
@@ -456,7 +687,7 @@ func PairHandler(w http.ResponseWriter, r *http.Request) {
defer close(priceChan)
defer close(errChan)
- ctx, cancel := context.WithTimeout(r.Context(), getTimeout*time.Second)
+ ctx, cancel := context.WithTimeout(request.Context(), getTimeout*time.Second)
defer cancel()
waitGroup.Add(2) //nolint: mnd,gomnd
@@ -500,11 +731,15 @@ func PairHandler(w http.ResponseWriter, r *http.Request) {
log.Info().Msg(fmt.Sprintf("%v", ratio))
- err = json.NewEncoder(w).Encode(map[string]interface{}{"ratio": ratio})
+ err = json.NewEncoder(writer).Encode(map[string]interface{}{"ratio": ratio})
if err != nil {
log.Error().Err(err).Send()
- http.Error(w, "internal server error", http.StatusInternalServerError)
+ http.Error(writer, "internal server error", http.StatusInternalServerError)
+
+ return err
}
+
+ return nil
}
type alertType struct {
@@ -563,7 +798,7 @@ func getTickers() tickersType {
return tickers
}
-func alertManagerWorker(alert alertType) {
+func alertManagerWorker(alert alertType, tgChannelID int64) {
expression, err := govaluate.NewEvaluableExpression(alert.Expr)
if err != nil {
log.Error().Err(err)
@@ -633,47 +868,44 @@ func alertManagerWorker(alert alertType) {
return
}
- token := os.Getenv(telegramBotTokenEnvVar)
msgText := "notification " + alert.Expr + " has been triggered"
- tokenInt, err := strconv.ParseInt(token[1:len(token)-1], 10, 64)
-
if err == nil {
log.Error().Err(err)
}
- sendToTg("telebot:8000", msgText, tokenInt)
+ sendToTg(msgText, tgChannelID)
}
-func alertManager(alertsCheckInterval int64) {
+func alertManager(alertsCheckInterval int64, tgChannelID int64) {
for {
alerts := getAlerts()
log.Info().Msg(fmt.Sprintf("%v", alerts))
for alertIndex := range alerts.Alerts {
- go alertManagerWorker(alerts.Alerts[alertIndex])
+ go alertManagerWorker(alerts.Alerts[alertIndex], tgChannelID)
}
time.Sleep(time.Second * time.Duration(alertsCheckInterval))
}
}
-func tickerManager(tickerCheckInterval int64) {
+func tickerManager(tickerCheckInterval int64, tgChannelID int64) {
for {
tickers := getTickers()
log.Info().Msg(fmt.Sprintf("%v", tickers))
for tickerIndex := range tickers.Tickers {
- go tickerManagerWorker(tickers.Tickers[tickerIndex])
+ go tickerManagerWorker(tickers.Tickers[tickerIndex], tgChannelID)
}
time.Sleep(time.Second * time.Duration(tickerCheckInterval))
}
}
-func tickerManagerWorker(ticker tickerType) {
+func tickerManagerWorker(ticker tickerType, tgChannelID int64) {
var waitGroup sync.WaitGroup
priceChan := make(chan priceChanStruct, 1)
@@ -707,17 +939,11 @@ func tickerManagerWorker(ticker tickerType) {
log.Error().Err(errBadLogic)
}
- token := os.Getenv(telegramBotTokenEnvVar)
msgText := "ticker: " + ticker.Name + ":" + strconv.FormatFloat(price.price, 'f', -1, 64)
- tokenInt, err := strconv.ParseInt(token[1:len(token)-1], 10, 64)
log.Print(msgText)
- if err == nil {
- log.Error().Err(err)
- }
-
- sendToTg("telebot:8000", msgText, tokenInt)
+ sendToTg(msgText, tgChannelID)
}
type addAlertJSONType struct {
@@ -729,7 +955,10 @@ type tickerJSONType struct {
Name string `json:"name"`
}
-func tickerHandler(writer http.ResponseWriter, request *http.Request) {
+func (aw appWrapper) tickerHandler(echoCtx echo.Context) error {
+ writer := echoCtx.Response().Writer
+ request := echoCtx.Request()
+
addSecureHeaders(&writer)
handler := Handler{rdb: rdb}
@@ -748,9 +977,13 @@ func tickerHandler(writer http.ResponseWriter, request *http.Request) {
default:
http.Error(writer, "Method is not supported.", http.StatusNotFound)
}
+
+ return nil
}
-func healthHandler(writer http.ResponseWriter, request *http.Request) {
+func (aw appWrapper) healthHandler(echoCtx echo.Context) error {
+ writer := echoCtx.Response().Writer
+ request := echoCtx.Request()
var RedisError string
var HivedError string
@@ -799,117 +1032,197 @@ func healthHandler(writer http.ResponseWriter, request *http.Request) {
if request.Method != http.MethodGet {
http.Error(writer, "internal server error", http.StatusInternalServerError)
log.Error().Err(err)
+
+ return err
}
+
+ return nil
}
-func robotsHandler(writer http.ResponseWriter, _ *http.Request) {
- writer.Header().Add("Content-Type", "text/plain")
- addSecureHeaders(&writer)
+func setupLogging() {
+ zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
+}
- _, err := writer.Write([]byte("User-Agents: *\nDisallow: /\n"))
+func (aw appWrapper) postHandler(context echo.Context) error {
+ user, pass, ok := context.Request().BasicAuth()
+ if !ok {
+ return context.JSON(http.StatusUnauthorized, "unauthorized") //nolint: wrapcheck
+ }
+
+ userRecord, err := aw.app.Dao().FindAuthRecordByUsername("users", user)
if err != nil {
- log.Error().Err(err)
+ return context.JSON(http.StatusUnauthorized, "unauthorized") //nolint: wrapcheck
}
- http.Error(writer, "internal server error", http.StatusInternalServerError)
-}
+ if !userRecord.ValidatePassword(pass) {
+ return context.JSON(http.StatusUnauthorized, "unauthorized") //nolint: wrapcheck
+ }
-func startServer(gracefulWait time.Duration, flagPort string) {
- router := mux.NewRouter()
+ return context.JSON(http.StatusOK, "OK") //nolint: wrapcheck
+}
- cfg := &tls.Config{
- MinVersion: tls.VersionTLS13,
- PreferServerCipherSuites: true,
+func defaultPublicDir() string {
+ if strings.HasPrefix(os.Args[0], os.TempDir()) {
+ return "./pb_public"
}
- srv := &http.Server{
- Addr: "0.0.0.0:" + flagPort,
- WriteTimeout: time.Second * serverTLSWriteTimeout,
- ReadTimeout: time.Second * serverTLSReadTimeout,
- Handler: router,
- TLSConfig: cfg,
- }
+ return filepath.Join(os.Args[0], "../pb_public")
+}
- router.HandleFunc("/crypto/v1/health", healthHandler)
- router.HandleFunc("/crypto/v1/price", PriceHandler)
- router.HandleFunc("/crypto/v1/pair", PairHandler)
- router.HandleFunc("/crypto/v1/alert", alertHandler)
- router.HandleFunc("/crypto/v1/ticker", tickerHandler)
- router.HandleFunc("/crypto/v1/robots.txt", robotsHandler)
+func setRootCmds(app *pocketbase.PocketBase) RootCmds {
+ var rootCmds RootCmds
- go func() {
- var certPath, keyPath string
+ app.RootCmd.PersistentFlags().StringVar(
+ &rootCmds.hooksDir,
+ "hooksDir",
+ "",
+ "the directory with the JS app hooks",
+ )
- switch os.Getenv(serverDeploymentType) {
- case "deployment":
- certPath = "/etc/letsencrypt/live/api.terminaldweller.com/fullchain.pem"
- keyPath = "/etc/letsencrypt/live/api.terminaldweller.com/privkey.pem"
- case "test":
- certPath = "/certs/server.cert"
- keyPath = "/certs/server.key"
- default:
- log.Fatal().Err(errUnknownDeploymentKind)
- }
+ app.RootCmd.PersistentFlags().BoolVar(
+ &rootCmds.hooksWatch,
+ "hooksWatch",
+ true,
+ "auto restart the app on pb_hooks file change",
+ )
- if err := srv.ListenAndServeTLS(certPath, keyPath); err != nil {
- log.Fatal().Err(err).Send()
- }
- }()
+ app.RootCmd.PersistentFlags().IntVar(
+ &rootCmds.hooksPool,
+ "hooksPool",
+ 25,
+ "the total prewarm goja.Runtime instances for the JS app hooks execution",
+ )
- c := make(chan os.Signal, 1)
+ app.RootCmd.PersistentFlags().StringVar(
+ &rootCmds.migrationsDir,
+ "migrationsDir",
+ "",
+ "the directory with the user defined migrations",
+ )
- signal.Notify(c, os.Interrupt)
- <-c
+ app.RootCmd.PersistentFlags().BoolVar(
+ &rootCmds.automigrate,
+ "automigrate",
+ true,
+ "enable/disable auto migrations",
+ )
- ctx, cancel := context.WithTimeout(context.Background(), gracefulWait)
- defer cancel()
+ app.RootCmd.PersistentFlags().StringVar(
+ &rootCmds.publicDir,
+ "publicDir",
+ defaultPublicDir(),
+ "the directory to serve static files",
+ )
- if err := srv.Shutdown(ctx); err != nil {
- log.Error().Err(err)
- } else {
- log.Info().Msg("gracefully shut down the server")
- }
+ app.RootCmd.PersistentFlags().BoolVar(
+ &rootCmds.indexFallback,
+ "indexFallback",
+ true,
+ "fallback the request to index.html on missing static path (eg. when pretty urls are used with SPA)",
+ )
+
+ app.RootCmd.PersistentFlags().IntVar(
+ &rootCmds.queryTimeout,
+ "queryTimeout",
+ 30,
+ "the default SELECT queries timeout in seconds",
+ )
+
+ return rootCmds
}
-func setupLogging() {
- zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
+func startPocketbaseApp() {
+ app := pocketbase.New()
+
+ aw := appWrapper{app: app}
+
+ app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
+ e.Router.POST("/", aw.postHandler)
+ e.Router.GET("/health", aw.healthHandler)
+ e.Router.GET("/api/crypto/v1/price", aw.PriceHandler)
+ e.Router.GET("/api/crypto/v1/pair", aw.PairHandler)
+
+ e.Router.GET("/api/crypto/v1/alert", aw.alertHandler)
+ e.Router.PUT("/api/crypto/v1/alert", aw.alertHandler)
+ e.Router.POST("/api/crypto/v1/alert", aw.alertHandler)
+ e.Router.PATCH("/api/crypto/v1/alert", aw.alertHandler)
+ e.Router.DELETE("/api/crypto/v1/alert", aw.alertHandler)
+
+ e.Router.GET("/api/crypto/v1/ticker", aw.tickerHandler)
+ e.Router.PUT("/api/crypto/v1/ticker", aw.tickerHandler)
+ e.Router.POST("/api/crypto/v1/ticker", aw.tickerHandler)
+ e.Router.PATCH("/api/crypto/v1/ticker", aw.tickerHandler)
+ e.Router.DELETE("/api/crypto/v1/ticker", aw.tickerHandler)
+
+ return nil
+ })
+
+ rootCmds := setRootCmds(app)
+
+ err := app.RootCmd.ParseFlags(os.Args[1:])
+ if err != nil {
+ log.Fatal().Err(err)
+ }
+
+ jsvm.MustRegister(app, jsvm.Config{
+ MigrationsDir: rootCmds.migrationsDir,
+ HooksDir: rootCmds.hooksDir,
+ HooksWatch: rootCmds.hooksWatch,
+ HooksPoolSize: rootCmds.hooksPoolSize,
+ })
+
+ migratecmd.MustRegister(app, app.RootCmd, migratecmd.Config{
+ TemplateLang: migratecmd.TemplateLangJS,
+ Automigrate: rootCmds.automigrate,
+ Dir: rootCmds.migrationsDir,
+ })
+
+ ghupdate.MustRegister(app, app.RootCmd, ghupdate.Config{})
+
+ app.OnAfterBootstrap().PreAdd(func(_ *core.BootstrapEvent) error {
+ app.Dao().ModelQueryTimeout = time.Duration(rootCmds.queryTimeout) * time.Second
+
+ return nil
+ })
+
+ app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
+ e.Router.GET("/*", apis.StaticDirectoryHandler(os.DirFS(rootCmds.publicDir), rootCmds.indexFallback))
+
+ return nil
+ })
+
+ if err := app.Start(); err != nil {
+ log.Fatal().Err(err)
+ }
}
func main() {
- var gracefulWait time.Duration
-
- flagPort := flag.String("port", "8008", "determined the port the sercice runs on")
- redisAddress := flag.String("redisaddress", "redis:6379", "determines the address of the redis instance")
- redisPassword := flag.String("redispassword", "", "determines the password of the redis db")
- redisDB := flag.Int64("redisdb", 0, "determines the db number")
- alertsCheckInterval := flag.Int64(
- "alertinterval",
- alertCheckIntervalDefault,
- "in seconds, the amount of time between alert checks")
- tickerCheckInterval := flag.Int64(
- "interval",
- tickerCheckIntervalDefault,
- "in seconds, the amount of time between alert checks")
-
- flag.DurationVar(
- &gracefulWait, "gracefulwait",
- time.Second*defaultGracefulShutdown,
- "the duration to wait during the graceful shutdown")
-
- flag.Parse()
+ data, err := os.ReadFile("/hived/hived.toml")
+ if err != nil {
+ log.Fatal().Err(err)
+ }
+
+ var config HivedConfig
+
+ _, err = toml.Decode(string(data), &config)
+ if err != nil {
+ log.Fatal().Err(err)
+ }
+
+ fmt.Println("XXX:", config)
rdb = redis.NewClient(&redis.Options{
- Addr: *redisAddress,
- Password: *redisPassword,
- DB: int(*redisDB),
+ Addr: config.KeydbAddress,
+ Password: config.KeydbPassword,
+ DB: config.KeydbDB,
})
defer rdb.Close()
setupLogging()
- go alertManager(*alertsCheckInterval)
+ go alertManager(config.AlertsCheckInterval, config.TelegramChannelID)
- go tickerManager(*tickerCheckInterval)
+ go tickerManager(config.TickerCheckInterval, config.TelegramChannelID)
- startServer(gracefulWait, *flagPort)
+ startPocketbaseApp()
}
diff --git a/hived/hived.toml b/hived/hived.toml
new file mode 100644
index 0000000..abddb19
--- /dev/null
+++ b/hived/hived.toml
@@ -0,0 +1,7 @@
+keydbAddress = "keydb:6379"
+keydbPassword = ""
+keydbDB = 0
+alertsCheckInterval = 600
+tickerCheckInterval = 600
+cacheDuration = 600
+telegramChannelID =
diff --git a/nginx.conf b/nginx.conf
new file mode 100644
index 0000000..f6afdd2
--- /dev/null
+++ b/nginx.conf
@@ -0,0 +1,35 @@
+events {
+ worker_connections 1024;
+}
+
+http {
+ include /etc/nginx/mime.types;
+ server_tokens off;
+ limit_req_zone $binary_remote_addr zone=one:10m rate=30r/m;
+ server {
+ listen 443 ssl;
+ keepalive_timeout 60;
+ charset utf-8;
+ ssl_certificate /etc/letsencrypt/live/api.terminaldweller.com/fullchain.pem;
+ ssl_certificate_key /etc/letsencrypt/live/api.terminaldweller.com/privkey.pem;
+ ssl_ciphers HIGH:!aNULL:!MD5:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
+ ssl_protocols TLSv1.3;
+ ssl_session_cache shared:SSL:50m;
+ ssl_session_timeout 1d;
+ ssl_session_tickets off;
+ ssl_prefer_server_ciphers on;
+ sendfile on;
+ tcp_nopush on;
+ add_header X-Content-Type-Options "nosniff" always;
+ add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
+ add_header X-Frame-Options SAMEORIGIN always;
+ add_header X-XSS-Protection "1; mode=block" always;
+ add_header Referrer-Policy "no-referrer";
+ fastcgi_hide_header X-Powered-By;
+
+ error_page 401 403 404 /404.html;
+ location / {
+ proxy_pass http://hived:8090;
+ }
+ }
+}
diff --git a/telebot/.golangci.yml b/telebot/.golangci.yml
deleted file mode 100644
index c31a774..0000000
--- a/telebot/.golangci.yml
+++ /dev/null
@@ -1,19 +0,0 @@
-run:
- concurrency: 16
- timeout: 5m
- modules-download-mode: readonly
- allow-parallel-runners: true
- allow-serial-runners: true
- go: '1.22'
-linters-settings:
- depguard:
- rules:
- srcs:
- listMode: "Strict"
- allow:
- - $gostd
- - github.com/go-telegram-bot-api/telegram-bot-api
- - github.com/rs/zerolog/log
- - github.com/terminaldweller/grpc/telebot/v1
- - golang.org/x/net/proxy
- - google.golang.org/grpc
diff --git a/telebot/Dockerfile b/telebot/Dockerfile
deleted file mode 100644
index 5b573f1..0000000
--- a/telebot/Dockerfile
+++ /dev/null
@@ -1,21 +0,0 @@
-FROM golang:1.22-alpine3.19 as builder
-RUN apk update && apk upgrade
-RUN apk add go git
-COPY go.* /telebot/
-RUN cd /telebot && go mod download
-COPY *.go /telebot/
-RUN cd /telebot && go build
-
-FROM alpine:3.19 as certbuilder
-RUN apk add openssl
-WORKDIR /certs
-RUN openssl req -nodes -new -x509 -subj="/C=US/ST=Denial/L=springfield/O=Dis/CN=localhost" -keyout server.key -out server.cert
-
-# FROM gcr.io/distroless/static-debian10
-FROM alpine:3.19
-COPY --from=certbuilder /certs /certs
-COPY --from=builder /telebot/telebot /telebot/
-COPY ./docker-entrypoint.sh /telebot/
-ENV ALL_PROXY=
-ENV HTTPS_PROXY=
-ENTRYPOINT ["/telebot/docker-entrypoint.sh"]
diff --git a/telebot/docker-entrypoint.sh b/telebot/docker-entrypoint.sh
deleted file mode 100755
index c2a6d0a..0000000
--- a/telebot/docker-entrypoint.sh
+++ /dev/null
@@ -1,6 +0,0 @@
-#!/usr/bin/env sh
-set -ex
-
-export $(cat /run/secrets/tg_bot_token)
-
-"/telebot/telebot" "$@"
diff --git a/telebot/go.mod b/telebot/go.mod
deleted file mode 100644
index 32444e9..0000000
--- a/telebot/go.mod
+++ /dev/null
@@ -1,22 +0,0 @@
-module telebot
-
-go 1.22
-
-require (
- github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible
- github.com/rs/zerolog v1.31.0
- github.com/terminaldweller/grpc v1.0.3
- golang.org/x/net v0.19.0
- google.golang.org/grpc v1.59.0
-)
-
-require (
- github.com/golang/protobuf v1.5.3 // indirect
- github.com/mattn/go-colorable v0.1.13 // indirect
- github.com/mattn/go-isatty v0.0.20 // indirect
- github.com/technoweenie/multipartstreamer v1.0.1 // indirect
- golang.org/x/sys v0.15.0 // indirect
- golang.org/x/text v0.14.0 // indirect
- google.golang.org/genproto/googleapis/rpc v0.0.0-20231127180814-3a041ad873d4 // indirect
- google.golang.org/protobuf v1.31.0 // indirect
-)
diff --git a/telebot/go.sum b/telebot/go.sum
deleted file mode 100644
index d720e6d..0000000
--- a/telebot/go.sum
+++ /dev/null
@@ -1,149 +0,0 @@
-cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
-cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
-github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
-github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
-github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
-github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
-github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
-github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
-github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
-github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
-github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
-github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
-github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
-github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
-github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
-github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
-github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
-github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
-github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
-github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
-github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
-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/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
-github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
-github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
-github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
-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.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
-github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
-github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
-github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
-github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
-github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
-github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
-github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
-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.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
-github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
-github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
-github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
-github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
-github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
-github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
-github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
-github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
-github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
-github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
-github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
-github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A=
-github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
-github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
-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/terminaldweller/grpc v1.0.3 h1:yCRm0HKRD4M87CBmmO5KjkSFRq4X2lzHLJRH1ApzeiE=
-github.com/terminaldweller/grpc v1.0.3/go.mod h1:pYpuXZw8rHZwTABVEVZEfErFr+PyEhAaSrFm7y1yvTo=
-go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
-golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
-golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
-golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
-golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
-golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
-golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
-golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
-golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/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-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
-golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
-golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
-golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
-golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
-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/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
-google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
-google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
-google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
-google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20231127180814-3a041ad873d4 h1:DC7wcm+i+P1rN3Ff07vL+OndGg5OhNddHyTA+ocPqYE=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20231127180814-3a041ad873d4/go.mod h1:eJVxU6o+4G1PSczBr85xmyvSNYAKvAYgkub40YGomFM=
-google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
-google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
-google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
-google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
-google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
-google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
-google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
-google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk=
-google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98=
-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.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
-google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
-google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
-google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
-google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
-google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
-google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
-google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
-google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
diff --git a/telebot/telebot.go b/telebot/telebot.go
deleted file mode 100644
index 91eebcb..0000000
--- a/telebot/telebot.go
+++ /dev/null
@@ -1,167 +0,0 @@
-package main
-
-import (
- "context"
- "flag"
- "fmt"
- "net"
- "net/http"
- "os"
- "strconv"
- "time"
-
- tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api"
- "github.com/rs/zerolog/log"
- pb "github.com/terminaldweller/grpc/telebot/v1"
- "golang.org/x/net/proxy"
- "google.golang.org/grpc"
-)
-
-var botChannelID = flag.Int64(
- "botchannelid",
- 146328407, //nolint: mnd,gomnd
- "determines the channel id the telgram bot should send messages to")
-
-const (
- telegramBotTokenEnvVar = "TELEGRAM_BOT_TOKEN" //nolint: gosec
- httpClientTimeout = 5
-)
-
-type server struct {
- pb.UnimplementedNotificationServiceServer
-}
-
-func GetProxiedClient() (*http.Client, error) {
- var isProxied bool
-
- proxyURL := os.Getenv("ALL_PROXY")
- if proxyURL == "" {
- proxyURL = os.Getenv("HTTPS_PROXY")
- }
-
- if proxyURL == "" {
- isProxied = false
- }
-
- var dialerProxy proxy.Dialer
-
- var dialer net.Dialer
-
- var err error
-
- if isProxied {
- dialerProxy, err = proxy.SOCKS5("tcp", proxyURL, nil, proxy.Direct)
- if err != nil {
- return nil, fmt.Errorf("[GetProxiedClient] : %w", err)
- }
- } else {
- dialer = net.Dialer{
- Timeout: 5 * time.Second, //nolint: mnd,gomnd
- }
-
- if err != nil {
- return nil, fmt.Errorf("[GetProxiedClient] : %w", err)
- }
- }
-
- dialContext := func(ctx context.Context, network, address string) (net.Conn, error) {
- if isProxied {
- netConn, err := dialerProxy.Dial(network, address)
- if err == nil {
- return netConn, nil
- }
-
- return netConn, fmt.Errorf("[dialContext] : %w", err)
- } else {
- netConn, err := dialer.Dial(network, address)
- if err == nil {
- return netConn, nil
- }
-
- return netConn, fmt.Errorf("[dialContext] : %w", err)
- }
- }
-
- transport := &http.Transport{
- DialContext: dialContext,
- DisableKeepAlives: true,
- }
- client := &http.Client{
- Transport: transport,
- Timeout: httpClientTimeout * time.Second,
- CheckRedirect: nil,
- Jar: nil,
- }
-
- return client, nil
-}
-
-func getTGBot() *tgbotapi.BotAPI {
- token := os.Getenv(telegramBotTokenEnvVar)
-
- // client, err := GetProxiedClient()
- // if err != nil {
- // log.Fatal().Err(err)
- // }
-
- // bot, err := tgbotapi.NewBotAPIWithClient(token[1:len(token)-1], client)
- bot, err := tgbotapi.NewBotAPI(token[1 : len(token)-1])
- if err != nil {
- log.Fatal().Err(err).Send()
- }
-
- return bot
-}
-
-func sendMessage(bot *tgbotapi.BotAPI, msgText string, channelID int64) error {
- msg := tgbotapi.NewMessage(channelID, msgText)
- _, err := bot.Send(msg)
-
- return err
-}
-
-func (s *server) Notify(
- ctx context.Context,
- NotificationRequest *pb.NotificationRequest,
-) (*pb.NotificationResponse, error) {
- var err error
-
- tgbotapi := getTGBot()
-
- if NotificationRequest.ChannelId == 0 {
- err = sendMessage(tgbotapi, NotificationRequest.NotificationText, *botChannelID)
- } else {
- err = sendMessage(tgbotapi, NotificationRequest.NotificationText, NotificationRequest.ChannelId)
- }
-
- if err != nil {
- return &pb.NotificationResponse{Error: err.Error(), IsOK: false}, err
- }
-
- return &pb.NotificationResponse{Error: "", IsOK: true}, nil
-}
-
-func startServer(port uint16) {
- listener, err := net.Listen("tcp", fmt.Sprintf("0.0.0.0:%d", port))
- if err != nil {
- log.Fatal().Err(err).Send()
- }
-
- var opts []grpc.ServerOption
-
- grpcServer := grpc.NewServer(opts...)
- pb.RegisterNotificationServiceServer(grpcServer, &server{})
-
- if err := grpcServer.Serve(listener); err != nil {
- log.Fatal().Err(err).Send()
- }
-}
-
-func main() {
- flagPort := flag.String("port", "8000", "determines the port the service runs on")
- flag.Parse()
-
- port, _ := strconv.Atoi(*flagPort)
-
- startServer(uint16(port))
-}
diff --git a/test/endpoints.sh b/test/endpoints.sh
index 2b73614..0e7a96c 100755
--- a/test/endpoints.sh
+++ b/test/endpoints.sh
@@ -1,22 +1,23 @@
-#!/usr/bin/env sh
-set -e
-set -x
+#!/bin/sh
+set -ex
-curl -k -X GET "https://localhost:10008/crypto/v1/price?name=PEPE&unit=USD"
-curl -k -X GET "https://localhost:10008/crypto/v1/pair?one=ETH&two=CAKE&multiplier=4.0"
-curl -k -X POST -H "Content-Type: application/json" -d '{"name":"alert1", "expr":"ETH>CAKE"}' https://localhost:10008/crypto/v1/alert
+# curl -k -X GET "https://localhost:8009/crypto/v1/arb/gecko?name=ethereum&unit=usd"
+# curl -k -X GET "https://localhost:8009/crypto/v1/arb/coincap?name=ethereum"
+curl -k -X GET "https://localhost:10008/api/crypto/v1/price?name=PEPE&unit=USD"
+curl -k -X GET "https://localhost:10008/api/crypto/v1/pair?one=ETH&two=CAKE&multiplier=4.0"
+curl -k -X POST -H "Content-Type: application/json" -d '{"name":"alert1", "expr":"ETH>CAKE"}' https://localhost:10008/api/crypto/v1/alert
# alert
-curl -k -X GET -H "Content-Type: application/json" "https://localhost:10008/crypto/v1/alert?key=alert1"
-curl -k -X DELETE -H "Content-Type: application/json" "https://localhost:10008/crypto/v1/alert?key=alert1"
-curl -k -X POST -H "Content-Type: application/json" -d '{"name":"alert1", "expr":"ETH>CAKE"}' https://localhost:10008/crypto/v1/alert
+curl -k -X GET -H "Content-Type: application/json" "https://localhost:10008/api/crypto/v1/alert?key=alert1"
+curl -k -X DELETE -H "Content-Type: application/json" "https://localhost:10008/api/crypto/v1/alert?key=alert1"
+curl -k -X POST -H "Content-Type: application/json" -d '{"name":"alert1", "expr":"ETH>CAKE"}' https://localhost:10008/api/crypto/v1/alert
# ticker
-curl -k -X POST -H "Content-Type: application/json" -d '{"name":"ETH"}' https://localhost:10008/crypto/v1/ticker
-curl -k -X POST -H "Content-Type: application/json" -d '{"name":"BTC"}' https://localhost:10008/crypto/v1/ticker
-curl -k -X POST -H "Content-Type: application/json" -d '{"name":"XMR"}' https://localhost:10008/crypto/v1/ticker
-curl -k -X POST -H "Content-Type: application/json" -d '{"name":"STX"}' https://localhost:10008/crypto/v1/ticker
-curl -k -X POST -H "Content-Type: application/json" -d '{"name":"LINK"}' https://localhost:10008/crypto/v1/ticker
-curl -k -X POST -H "Content-Type: application/json" -d '{"name":"PEPE"}' https://localhost:10008/crypto/v1/ticker
-curl -k -X GET -H "Content-Type: application/json" https://localhost:10008/crypto/v1/ticker?key=ETH
-curl -k -X DELETE -H "Content-Type: application/json" https://localhost:10008/crypto/v1/ticker?key=ETH
+curl -k -X POST -H "Content-Type: application/json" -d '{"name":"ETH"}' https://localhost:10008/api/crypto/v1/ticker
+curl -k -X POST -H "Content-Type: application/json" -d '{"name":"BTC"}' https://localhost:10008/api/crypto/v1/ticker
+curl -k -X POST -H "Content-Type: application/json" -d '{"name":"XMR"}' https://localhost:10008/api/crypto/v1/ticker
+curl -k -X POST -H "Content-Type: application/json" -d '{"name":"STX"}' https://localhost:10008/api/crypto/v1/ticker
+curl -k -X POST -H "Content-Type: application/json" -d '{"name":"LINK"}' https://localhost:10008/api/crypto/v1/ticker
+curl -k -X POST -H "Content-Type: application/json" -d '{"name":"PEPE"}' https://localhost:10008/api/crypto/v1/ticker
+curl -k -X GET -H "Content-Type: application/json" https://localhost:10008/api/crypto/v1/ticker?key=ETH
+curl -k -X DELETE -H "Content-Type: application/json" https://localhost:10008/api/crypto/v1/ticker?key=ETH