From 43b940673c36c03f47a63ffe2073eecbd4fc804d Mon Sep 17 00:00:00 2001 From: Andrei Kurilov <18027129+akurilov@users.noreply.github.com> Date: Sat, 18 May 2024 00:02:15 +0300 Subject: [PATCH] feat: webhooks --- .github/workflows/staging.yaml | 2 +- Dockerfile | 2 +- api/grpc/tgbot/controller.go | 76 ++-- api/grpc/tgbot/controller_test.go | 1 + api/http/reader/callback.go | 29 ++ api/http/reader/logging.go | 50 +++ api/http/reader/service.go | 105 ++++++ config/config.go | 35 +- config/config_test.go | 3 - go.mod | 64 ++-- go.sum | 155 ++++---- .../templates/{sts.yaml => deployment.yaml} | 46 +-- helm/bot-telegram/templates/ingress-grpc.yaml | 61 ---- helm/bot-telegram/templates/service.yaml | 11 +- helm/bot-telegram/values.yaml | 31 +- main.go | 109 ++---- scripts/cover.sh | 2 +- service/chats/handler.go | 178 ++++++++++ service/chats/reader.go | 318 ----------------- service/chats/storage.go | 17 - service/chats/storage_mongo.go | 286 --------------- service/chats/storage_mongo_test.go | 334 ------------------ service/chats/user_left.go | 22 -- service/messages/format.go | 24 +- service/subscriptions/list.go | 20 +- service/subscriptions/start.go | 41 +-- service/subscriptions/stop.go | 18 +- 27 files changed, 622 insertions(+), 1418 deletions(-) create mode 100644 api/http/reader/callback.go create mode 100644 api/http/reader/logging.go create mode 100644 api/http/reader/service.go rename helm/bot-telegram/templates/{sts.yaml => deployment.yaml} (78%) delete mode 100644 helm/bot-telegram/templates/ingress-grpc.yaml create mode 100644 service/chats/handler.go delete mode 100644 service/chats/reader.go delete mode 100644 service/chats/storage.go delete mode 100644 service/chats/storage_mongo.go delete mode 100644 service/chats/storage_mongo_test.go delete mode 100644 service/chats/user_left.go diff --git a/.github/workflows/staging.yaml b/.github/workflows/staging.yaml index 09ce342..0134910 100644 --- a/.github/workflows/staging.yaml +++ b/.github/workflows/staging.yaml @@ -62,4 +62,4 @@ jobs: run: | helm upgrade --install ${COMPONENT} ${COMPONENT}-0.0.0.tgz \ --values helm/bot-telegram/values-gke-cluster-0.yaml \ - --set podAnnotations.commit=$(git rev-parse --short HEAD),api.telegram.support.chat.id=${{ secrets.TG_BOT_SUPPORT_CHAT_ID }},api.telegram.webhook.token=${{ secrets.API_TELEGRAM_WEBHOOK_TOKEN }},chats.db.password.secret.enabled=false,chats.db.protocol=mongodb+srv,chats.db.tls.enabled=true,chats.db.tls.insecure=true,chats.db.hostname=${{ secrets.DB_HOST_DEMO }},chats.db.username=${{ secrets.DB_USERNAME_DEMO }},chats.db.password.raw=${{ secrets.DB_PASSWORD_DEMO }} + --set podAnnotations.commit=$(git rev-parse --short HEAD),api.telegram.support.chat.id=${{ secrets.TG_BOT_SUPPORT_CHAT_ID }},api.telegram.webhook.token=${{ secrets.API_TELEGRAM_WEBHOOK_TOKEN }} diff --git a/Dockerfile b/Dockerfile index 80b99f7..cc45dfc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.22.1-alpine3.19 AS builder +FROM golang:1.22.3-alpine3.19 AS builder WORKDIR /go/src/bot-telegram COPY . . RUN \ diff --git a/api/grpc/tgbot/controller.go b/api/grpc/tgbot/controller.go index 020a4dc..d381d39 100644 --- a/api/grpc/tgbot/controller.go +++ b/api/grpc/tgbot/controller.go @@ -4,22 +4,18 @@ import ( "context" "encoding/json" "fmt" + "github.com/awakari/bot-telegram/api/http/reader" "github.com/awakari/bot-telegram/service" - "github.com/awakari/bot-telegram/service/chats" "github.com/awakari/bot-telegram/service/messages" - "github.com/awakari/bot-telegram/service/subscriptions" "github.com/awakari/client-sdk-go/api" - "github.com/awakari/client-sdk-go/model/subscription" tgverifier "github.com/electrofocus/telegram-auth-verifier" "google.golang.org/grpc/codes" - "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/timestamppb" "gopkg.in/telebot.v3" "log/slog" "strconv" "strings" - "time" ) type Controller interface { @@ -27,34 +23,35 @@ type Controller interface { } type controller struct { - secretToken []byte - cp messages.ChanPostHandler - chatStor chats.Storage - log *slog.Logger - clientAwk api.Client - tgBot *telebot.Bot - msgFmt messages.Format + secretToken []byte + cp messages.ChanPostHandler + svcReader reader.Service + urlCallbackBase string + log *slog.Logger + clientAwk api.Client + tgBot *telebot.Bot + msgFmt messages.Format } -const intervalDefault = 1 * time.Minute - func NewController( secretToken []byte, cp messages.ChanPostHandler, - chatStor chats.Storage, + svcReader reader.Service, + urlCallbackBase string, log *slog.Logger, clientAwk api.Client, tgBot *telebot.Bot, msgFmt messages.Format, ) Controller { return controller{ - secretToken: secretToken, - cp: cp, - chatStor: chatStor, - log: log, - clientAwk: clientAwk, - tgBot: tgBot, - msgFmt: msgFmt, + secretToken: secretToken, + cp: cp, + svcReader: svcReader, + urlCallbackBase: urlCallbackBase, + log: log, + clientAwk: clientAwk, + tgBot: tgBot, + msgFmt: msgFmt, } } @@ -105,7 +102,6 @@ func (c controller) ListChannels(ctx context.Context, req *ListChannelsRequest) func (c controller) Subscribe(ctx context.Context, req *SubscribeRequest) (resp *SubscribeResponse, err error) { subId := req.SubId - groupId := req.GroupId userId := req.UserId if !strings.HasPrefix(userId, service.PrefixUserId) { err = status.Error(codes.InvalidArgument, fmt.Sprintf("User id should have prefix: %s, got: %s", service.PrefixUserId, userId)) @@ -118,40 +114,18 @@ func (c controller) Subscribe(ctx context.Context, req *SubscribeRequest) (resp } } if err == nil { - chat := chats.Chat{ - Id: chatId, - SubId: subId, - GroupId: groupId, - UserId: userId, - MinInterval: intervalDefault, - } - err = c.chatStor.LinkSubscription(ctx, chat) + err = c.svcReader.CreateCallback(ctx, subId, reader.MakeCallbackUrl(c.urlCallbackBase, chatId)) err = encodeError(err) } - if err == nil { - u := telebot.Update{ - Message: &telebot.Message{ - Chat: &telebot.Chat{ - ID: chatId, - }, - }, - } - tgCtx := c.tgBot.NewContext(u) - r := chats.NewReader(tgCtx, c.clientAwk, c.chatStor, chatId, subId, groupId, userId, c.msgFmt, intervalDefault) - go r.Run(context.Background(), c.log) - groupIdCtx := metadata.AppendToOutgoingContext(ctx, service.KeyGroupId, groupId) - var d subscription.Data - d, err = c.clientAwk.ReadSubscription(groupIdCtx, userId, subId) - if err == nil { - _ = tgCtx.Send(fmt.Sprintf(subscriptions.MsgFmtChatLinked, d.Description, intervalDefault), telebot.ModeHTML, telebot.NoPreview) - } - } return } func (c controller) Unsubscribe(ctx context.Context, req *UnsubscribeRequest) (resp *UnsubscribeResponse, err error) { - chats.StopChatReader(req.SubId) - err = c.chatStor.UnlinkSubscription(ctx, req.SubId) + var cb reader.Callback + cb, err = c.svcReader.GetCallback(ctx, req.SubId) + if err == nil { + err = c.svcReader.DeleteCallback(ctx, req.SubId, cb.Url) + } err = encodeError(err) return } diff --git a/api/grpc/tgbot/controller_test.go b/api/grpc/tgbot/controller_test.go index 641a42c..fa7aa2f 100644 --- a/api/grpc/tgbot/controller_test.go +++ b/api/grpc/tgbot/controller_test.go @@ -30,6 +30,7 @@ func TestMain(m *testing.M) { []byte("6668123457:ZAJALGCBOGw8q9k2yBidb6kepmrBVGOrBLb"), messages.ChanPostHandler{}, nil, + "", slog.Default(), nil, nil, diff --git a/api/http/reader/callback.go b/api/http/reader/callback.go new file mode 100644 index 0000000..1c8959a --- /dev/null +++ b/api/http/reader/callback.go @@ -0,0 +1,29 @@ +package reader + +import ( + "fmt" + "strconv" + "strings" +) + +type Callback struct { + Url string `json:"url"` + Format string `json:"fmt"` +} + +func MakeCallbackUrl(urlBase string, chatId int64) string { + return urlBase + "/" + strconv.FormatInt(chatId, 10) +} + +func GetCallbackUrlChatId(cbUrl string) (chatId int64, err error) { + cbUrlParts := strings.Split(cbUrl, "/") + cbUrlPartsLen := len(cbUrlParts) + if cbUrlPartsLen < 1 { + err = fmt.Errorf("invalid callback url") + } + if err == nil { + chatIdStr := cbUrlParts[cbUrlPartsLen-1] + chatId, err = strconv.ParseInt(chatIdStr, 10, 64) + } + return +} diff --git a/api/http/reader/logging.go b/api/http/reader/logging.go new file mode 100644 index 0000000..b82e794 --- /dev/null +++ b/api/http/reader/logging.go @@ -0,0 +1,50 @@ +package reader + +import ( + "context" + "fmt" + "log/slog" +) + +type serviceLogging struct { + svc Service + log *slog.Logger +} + +func NewServiceLogging(svc Service, log *slog.Logger) Service { + return serviceLogging{ + svc: svc, + log: log, + } +} + +func (sl serviceLogging) CreateCallback(ctx context.Context, subId, url string) (err error) { + err = sl.svc.CreateCallback(ctx, subId, url) + ll := sl.logLevel(err) + sl.log.Log(ctx, ll, fmt.Sprintf("reader.CreateCallback(%s, %s): err=%s", subId, url, err)) + return +} + +func (sl serviceLogging) GetCallback(ctx context.Context, subId string) (cb Callback, err error) { + cb, err = sl.svc.GetCallback(ctx, subId) + ll := sl.logLevel(err) + sl.log.Log(ctx, ll, fmt.Sprintf("reader.GetCallback(%s): %+v, err=%s", subId, cb, err)) + return +} + +func (sl serviceLogging) DeleteCallback(ctx context.Context, subId, url string) (err error) { + err = sl.svc.DeleteCallback(ctx, subId, url) + ll := sl.logLevel(err) + sl.log.Log(ctx, ll, fmt.Sprintf("reader.DeleteCallback(%s, %s): err=%s", subId, url, err)) + return +} + +func (sl serviceLogging) logLevel(err error) (lvl slog.Level) { + switch err { + case nil: + lvl = slog.LevelInfo + default: + lvl = slog.LevelError + } + return +} diff --git a/api/http/reader/service.go b/api/http/reader/service.go new file mode 100644 index 0000000..637eb0f --- /dev/null +++ b/api/http/reader/service.go @@ -0,0 +1,105 @@ +package reader + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "net/http" +) + +type Service interface { + CreateCallback(ctx context.Context, subId, url string) (err error) + GetCallback(ctx context.Context, subId string) (cb Callback, err error) + DeleteCallback(ctx context.Context, subId, url string) (err error) +} + +type service struct { + clientHttp *http.Client + uriBase string +} + +const keyHubCallback = "hub.callback" +const KeyHubMode = "hub.mode" +const KeyHubTopic = "hub.topic" +const modeSubscribe = "subscribe" +const modeUnsubscribe = "unsubscribe" +const fmtTopicUri = "%s/sub/%s/%s" +const FmtJson = "json" + +var ErrInternal = errors.New("internal failure") +var ErrConflict = errors.New("conflict") +var ErrNotFound = errors.New("not found") + +func NewService(clientHttp *http.Client, uriBase string) Service { + return service{ + clientHttp: clientHttp, + uriBase: uriBase, + } +} + +func (svc service) CreateCallback(ctx context.Context, subId, callbackUrl string) (err error) { + err = svc.updateCallback(ctx, subId, callbackUrl, modeSubscribe) + return +} + +func (svc service) GetCallback(ctx context.Context, subId string) (cb Callback, err error) { + var resp *http.Response + resp, err = svc.clientHttp.Get(fmt.Sprintf("/callbacks/%s/%s", svc.uriBase, subId)) + switch err { + case nil: + defer resp.Body.Close() + switch resp.StatusCode { + case http.StatusOK: + err = json.NewDecoder(resp.Body).Decode(&cb) + if err != nil { + err = fmt.Errorf("%w: %s", ErrInternal, err) + } + case http.StatusNotFound: + err = ErrNotFound + default: + err = fmt.Errorf("%w: response status %d", ErrInternal, resp.StatusCode) + } + err = json.NewDecoder(resp.Body).Decode(&cb) + default: + err = fmt.Errorf("%w: %s", ErrInternal, err) + } + return +} + +func (svc service) DeleteCallback(ctx context.Context, subId, callbackUrl string) (err error) { + err = svc.updateCallback(ctx, subId, callbackUrl, modeUnsubscribe) + return +} + +func (svc service) updateCallback(_ context.Context, subId, url, mode string) (err error) { + topicUri := fmt.Sprintf(fmtTopicUri, svc.uriBase, FmtJson, subId) + data := map[string][]string{ + keyHubCallback: { + url, + }, + KeyHubMode: { + mode, + }, + KeyHubTopic: { + topicUri, + }, + } + var resp *http.Response + resp, err = svc.clientHttp.PostForm(topicUri, data) + switch err { + case nil: + switch resp.StatusCode { + case http.StatusAccepted, http.StatusNoContent: + case http.StatusNotFound: + err = fmt.Errorf("%w: callback not found for the subscription %s", ErrConflict, subId) + case http.StatusConflict: + err = fmt.Errorf("%w: callback already registered for the subscription %s", ErrConflict, subId) + default: + err = fmt.Errorf("%w: unexpected create callback response %d", ErrInternal, resp.StatusCode) + } + default: + err = fmt.Errorf("%w: %s", ErrInternal, err) + } + return +} diff --git a/config/config.go b/config/config.go index e1da897..c27be65 100644 --- a/config/config.go +++ b/config/config.go @@ -14,9 +14,6 @@ type Config struct { Messages struct { Uri string `envconfig:"API_MESSAGES_URI" default:"messages:50051" required:"true"` } - Metrics struct { - Port uint16 `envconfig:"API_METRICS_PORT" default:"9090" required:"true"` - } Telegram struct { Bot struct { Port uint16 `envconfig:"API_TELEGRAM_BOT_PORT" default:"50051" required:"true"` @@ -32,34 +29,15 @@ type Config struct { Token string `envconfig:"API_TELEGRAM_TOKEN" required:"true"` } Uri string `envconfig:"API_URI" default:"api:50051" required:"true"` + Reader ReaderConfig Writer struct { Uri string `envconfig:"API_WRITER_URI" default:"resolver:50051" required:"true"` } } - Chats ChatsConfig Payment PaymentConfig Log struct { Level int `envconfig:"LOG_LEVEL" default:"-4" required:"true"` } - Replica ReplicaConfig -} - -type ChatsConfig struct { - Db ChatsDbConfig -} - -type ChatsDbConfig struct { - Uri string `envconfig:"CHATS_DB_URI" default:"mongodb://localhost:27017/?retryWrites=true&w=majority" required:"true"` - Name string `envconfig:"CHATS_DB_NAME" default:"bot-telegram" required:"true"` - UserName string `envconfig:"CHATS_DB_USERNAME" default:""` - Password string `envconfig:"CHATS_DB_PASSWORD" default:""` - Table struct { - Name string `envconfig:"CHATS_DB_TABLE_NAME" default:"chats" required:"true"` - } - Tls struct { - Enabled bool `envconfig:"CHATS_DB_TLS_ENABLED" default:"false" required:"true"` - Insecure bool `envconfig:"CHATS_DB_TLS_INSECURE" default:"false" required:"true"` - } } type PaymentConfig struct { @@ -103,9 +81,14 @@ type SitesConfig struct { Uri string `envconfig:"API_SOURCE_SITES_URI" default:"source-sites:50051" required:"true"` } -type ReplicaConfig struct { - Range uint32 `envconfig:"REPLICA_RANGE" required:"true"` - Name string `envconfig:"REPLICA_NAME" required:"true"` +type ReaderConfig struct { + Uri string `envconfig:"API_READER_URI" default:"http://reader:8080/v1" required:"true"` + CallBack struct { + Protocol string `envconfig:"API_READER_CALLBACK_PROTOCOL" default:"http" required:"true"` + Host string `envconfig:"API_READER_CALLBACK_HOST" default:"bot-telegram" required:"true"` + Port uint16 `envconfig:"API_READER_CALLBACK_PORT" default:"8081" required:"true"` + Path string `envconfig:"API_READER_CALLBACK_PATH" default:"/v1/chat" required:"true"` + } } func NewConfigFromEnv() (cfg Config, err error) { diff --git a/config/config_test.go b/config/config_test.go index 959e837..a481cc7 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -22,7 +22,4 @@ func TestConfig(t *testing.T) { assert.Equal(t, uint16(45678), cfg.Api.Telegram.Bot.Port) assert.Equal(t, 4, cfg.Log.Level) assert.Equal(t, "yohoho", cfg.Payment.Provider.Token) - assert.Equal(t, "mongodb://localhost:27017/?retryWrites=true&w=majority", cfg.Chats.Db.Uri) - assert.Equal(t, "bot-telegram", cfg.Chats.Db.Name) - assert.Equal(t, "chats", cfg.Chats.Db.Table.Name) } diff --git a/go.mod b/go.mod index 2dc7e4c..5e47d72 100644 --- a/go.mod +++ b/go.mod @@ -4,48 +4,52 @@ go 1.22 require ( github.com/awakari/client-sdk-go v1.0.9 - github.com/cenkalti/backoff/v4 v4.2.1 + github.com/cenkalti/backoff/v4 v4.3.0 github.com/cloudevents/sdk-go/binding/format/protobuf/v2 v2.15.2 + github.com/cloudevents/sdk-go/v2 v2.15.2 github.com/electrofocus/telegram-auth-verifier v1.1.1 + github.com/gin-gonic/gin v1.10.0 github.com/google/uuid v1.6.0 github.com/kelseyhightower/envconfig v1.4.0 github.com/microcosm-cc/bluemonday v1.0.26 - github.com/prometheus/client_golang v1.19.0 - github.com/stretchr/testify v1.8.4 - go.mongodb.org/mongo-driver v1.12.1 - go.uber.org/ratelimit v0.3.0 - google.golang.org/grpc v1.62.1 - google.golang.org/protobuf v1.32.0 - gopkg.in/telebot.v3 v3.1.3 + github.com/stretchr/testify v1.9.0 + google.golang.org/grpc v1.64.0 + google.golang.org/protobuf v1.34.1 + gopkg.in/telebot.v3 v3.2.1 ) require ( github.com/aymerick/douceur v0.2.0 // indirect - github.com/benbjohnson/clock v1.3.0 // indirect - github.com/beorn7/perks v1.0.1 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/bytedance/sonic v1.11.6 // indirect + github.com/bytedance/sonic/loader v0.1.1 // indirect + github.com/cloudwego/base64x v0.1.4 // indirect + github.com/cloudwego/iasm v0.2.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/golang/protobuf v1.5.3 // indirect - github.com/golang/snappy v0.0.3 // indirect + github.com/gabriel-vasile/mimetype v1.4.3 // indirect + github.com/gin-contrib/sse v0.1.0 // 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.20.0 // indirect + github.com/goccy/go-json v0.10.2 // indirect github.com/gorilla/css v1.0.0 // indirect - github.com/klauspost/compress v1.13.6 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect - github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect - github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/cpuid/v2 v2.2.7 // indirect + github.com/kr/pretty v0.3.1 // indirect + github.com/leodido/go-urn v1.4.0 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pelletier/go-toml/v2 v2.2.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_model v0.5.0 // indirect - github.com/prometheus/common v0.48.0 // indirect - github.com/prometheus/procfs v0.12.0 // indirect - github.com/xdg-go/pbkdf2 v1.0.0 // indirect - github.com/xdg-go/scram v1.1.2 // indirect - github.com/xdg-go/stringprep v1.0.4 // indirect - github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect - golang.org/x/crypto v0.21.0 // indirect - golang.org/x/net v0.21.0 // indirect - golang.org/x/sync v0.6.0 // indirect - golang.org/x/sys v0.18.0 // indirect - golang.org/x/text v0.14.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 // indirect + github.com/rogpeppe/go-internal v1.10.0 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.2.12 // indirect + golang.org/x/arch v0.8.0 // indirect + golang.org/x/crypto v0.23.0 // indirect + golang.org/x/net v0.25.0 // indirect + golang.org/x/sys v0.20.0 // indirect + golang.org/x/text v0.15.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index ff00465..40232a6 100644 --- a/go.sum +++ b/go.sum @@ -73,20 +73,19 @@ github.com/awakari/client-sdk-go v1.0.9 h1:BC9UnTNuBL+Hgfi5l4fwdCMWnpSfBlPJL+krM github.com/awakari/client-sdk-go v1.0.9/go.mod h1:I83Qlnyr/GM/5nVFNX3MIakUC01W21b/YdaK+064rZQ= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= -github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= -github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= -github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0= +github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4= +github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM= +github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= 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.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -95,6 +94,12 @@ github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cloudevents/sdk-go/binding/format/protobuf/v2 v2.15.2 h1:FIvfKlS2mcuP0qYY6yzdIU9xdrRd/YMP0bNwFjXd0u8= github.com/cloudevents/sdk-go/binding/format/protobuf/v2 v2.15.2/go.mod h1:POsdVp/08Mki0WD9QvvgRRpg9CQ6zhjfRrBoEY8JFS8= +github.com/cloudevents/sdk-go/v2 v2.15.2 h1:54+I5xQEnI73RBhWHxbI1XJcqOFOVJN85vb41+8mHUc= +github.com/cloudevents/sdk-go/v2 v2.15.2/go.mod h1:lL7kSWAE/V8VI4Wh0jbL2v/jvqsm6tjmaQBSvxcv4uE= +github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= +github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= +github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= +github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= @@ -129,7 +134,13 @@ github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGE github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= +github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= +github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= +github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -140,10 +151,20 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9 github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= 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/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +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.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +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.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= +github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8= +github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goccy/go-yaml v1.9.5/go.mod h1:U/jl18uSupI5rdI2jmuCswEA2htH9eXfferR3KfscvA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -179,10 +200,6 @@ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= 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/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -273,6 +290,7 @@ github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCV github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= @@ -282,8 +300,10 @@ github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dv github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc= -github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= +github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= @@ -292,11 +312,15 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 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/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= @@ -310,7 +334,8 @@ github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcME github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= +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/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/microcosm-cc/bluemonday v1.0.26 h1:xbqSvqzQMeEHCqMi64VAs4d8uy6Mequs3rQ0k/Khz58= github.com/microcosm-cc/bluemonday v1.0.26/go.mod h1:JyzOCs9gkyQyjs+6h10UEVSe02CGwkhd72Xdqh78TWs= @@ -324,20 +349,21 @@ github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0= -github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas= +github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= +github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -350,35 +376,26 @@ github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.11.1 h1:+4eQaD7vAZ6DsfsxB15hbE0odUjGI5ARs9yskGu1v4s= github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU= -github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= -github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.26.0 h1:iMAkS2TDoNWnKM+Kopnx/8tnEStIfpYA0ur0xQzzhMQ= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE= -github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= -github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sagikazarmark/crypt v0.6.0/go.mod h1:U8+INwJo3nBv1m6A/8OBXAq7Jnpspk5AxSgDyEQcea8= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= @@ -394,6 +411,8 @@ github.com/spf13/viper v1.13.0/go.mod h1:Icm2xNL3/8uyh/wFuB1jI7TiTNKp8632Nwegu+z github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/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/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -403,30 +422,25 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.5/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= -github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= -github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= -github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY= -github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4= -github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8= -github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= -github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA= -github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= +github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.etcd.io/etcd/api/v3 v3.5.4/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A= go.etcd.io/etcd/client/pkg/v3 v3.5.4/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/v2 v2.305.4/go.mod h1:Ud+VUwIi9/uQHOMA+4ekToJ12lTxlv0zB/+DHwTGEbU= go.etcd.io/etcd/client/v3 v3.5.4/go.mod h1:ZaRkVgBZC+L+dLCjTcF1hRXpgZXQPOvnA/Ak/gq3kiY= -go.mongodb.org/mongo-driver v1.12.1 h1:nLkghSU8fQNaK7oUmDhQFsnrtcoNy7Z6LVFKsEecqgE= -go.mongodb.org/mongo-driver v1.12.1/go.mod h1:/rGBTebI3XYboVmgz+Wv3Bcbl3aD0QF9zl6kDDw18rQ= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -437,10 +451,13 @@ go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/ratelimit v0.3.0 h1:IdZd9wqvFXnvLvSEBo0KPcGfkoBGNkpTHlrE3Rcjkjw= -go.uber.org/ratelimit v0.3.0/go.mod h1:So5LG7CV1zWpY1sHe+DXTJqQvOx+FFPFaAs2SnoyBaI= +go.uber.org/zap v1.17.0 h1:MTjgFu6ZLKvY6Pvaqk97GlxNBuMpV4Hy/3P6tRGlI2U= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc= +golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -449,12 +466,10 @@ golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= -golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +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/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -490,7 +505,6 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 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-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -538,9 +552,8 @@ golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= -golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +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-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -573,9 +586,6 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220513210516-0976fa681c29/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.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= -golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -654,9 +664,10 @@ golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/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.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +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.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/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -668,10 +679,8 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/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.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -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/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.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -730,7 +739,6 @@ golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -861,8 +869,8 @@ google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 h1:AjyfHzEPEFp/NpvfN5g+KDla3EMojjhRVZc1i7cj+oM= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 h1:NnYq6UN9ReLM9/Y01KWNOWyI5xQ9kbIms5GGJVwS/Yc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -893,8 +901,8 @@ google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ5 google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk= -google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= +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/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= 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= @@ -910,19 +918,18 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 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.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= -google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +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/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 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-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 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/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/telebot.v3 v3.1.3 h1:T+CTyOWpZMqp3ALHSweNgp1awQ9nMXdRAMpe/r6x9/s= -gopkg.in/telebot.v3 v3.1.3/go.mod h1:GJKwwWqp9nSkIVN51eRKU78aB5f5OnQuWdwiIZfPbko= +gopkg.in/telebot.v3 v3.2.1 h1:3I4LohaAyJBiivGmkfB+CiVu7QFOWkuZ4+KHgO/G3rs= +gopkg.in/telebot.v3 v3.2.1/go.mod h1:GJKwwWqp9nSkIVN51eRKU78aB5f5OnQuWdwiIZfPbko= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 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= @@ -942,7 +949,9 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/helm/bot-telegram/templates/sts.yaml b/helm/bot-telegram/templates/deployment.yaml similarity index 78% rename from helm/bot-telegram/templates/sts.yaml rename to helm/bot-telegram/templates/deployment.yaml index 1abd702..075f39e 100644 --- a/helm/bot-telegram/templates/sts.yaml +++ b/helm/bot-telegram/templates/deployment.yaml @@ -1,5 +1,5 @@ apiVersion: apps/v1 -kind: StatefulSet +kind: Deployment metadata: name: {{ include "bot-telegram.fullname" . }} labels: @@ -36,10 +36,18 @@ spec: value: "{{ .Values.api.admin.uri }}" - name: API_MESSAGES_URI value: "{{ .Values.api.messages.uri }}" - - name: API_METRICS_PORT - value: "{{ .Values.service.metrics.port }}" - name: API_WRITER_URI value: "{{ .Values.api.writer.uri }}" + - name: API_READER_URI + value: {{ .Values.api.reader.uri }} + - name: API_READER_CALLBACK_PROTOCOL + value: {{ .Values.api.reader.callback.protocol }} + - name: API_READER_CALLBACK_HOST + value: {{ include "bot-telegram.fullname" . }} + - name: API_READER_CALLBACK_PORT + value: {{ .Values.service.callback.port }} + - name: API_READER_CALLBACK_PATH + value: {{ .Values.api.reader.callback.path }} {{- range .Values.ingress.hosts }} - name: API_TELEGRAM_WEBHOOK_HOST value: {{ .host }} @@ -63,28 +71,6 @@ spec: secretKeyRef: name: "{{ include "bot-telegram.fullname" . }}-tokens" key: telegram - - name: CHATS_DB_URI - value: "{{ .Values.chats.db.protocol }}://{{ .Values.chats.db.hostname }}/?retryWrites=true&w=majority" - - name: CHATS_DB_NAME - value: {{ .Values.chats.db.name }} - - name: CHATS_DB_USERNAME - value: {{ .Values.chats.db.username }} - {{- if .Values.chats.db.password.secret.enabled }} - - name: CHATS_DB_PASSWORD - valueFrom: - secretKeyRef: - name: "{{ .Values.chats.db.password.secret.name }}" - key: "{{ .Values.chats.db.password.secret.key }}" - {{- else }} - - name: CHATS_DB_PASSWORD - value: "{{ .Values.chats.db.password.raw }}" - {{- end }} - - name: CHATS_DB_TABLE_NAME - value: {{ .Values.chats.db.table.name }} - - name: CHATS_DB_TLS_ENABLED - value: "{{ .Values.chats.db.tls.enabled }}" - - name: CHATS_DB_TLS_INSECURE - value: "{{ .Values.chats.db.tls.insecure }}" - name: LOG_LEVEL value: "{{ .Values.log.level }}" - name: PAYMENT_BACKOFF_FACTOR @@ -117,12 +103,6 @@ spec: secretKeyRef: name: "{{ include "bot-telegram.fullname" . }}-tokens" key: payment - - name: REPLICA_RANGE - value: "{{ .Values.replicaCount }}" - - name: REPLICA_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name securityContext: {{- toYaml .Values.securityContext | nindent 12 }} image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" @@ -133,8 +113,8 @@ spec: protocol: TCP - name: grpc containerPort: {{ .Values.service.grpc.port }} - - name: metrics - containerPort: {{ .Values.service.metrics.port }} + - name: callback + containerPort: {{ .Values.service.callback.port }} livenessProbe: grpc: port: {{ .Values.service.grpc.port }} diff --git a/helm/bot-telegram/templates/ingress-grpc.yaml b/helm/bot-telegram/templates/ingress-grpc.yaml deleted file mode 100644 index 9383cf9..0000000 --- a/helm/bot-telegram/templates/ingress-grpc.yaml +++ /dev/null @@ -1,61 +0,0 @@ -{{- if .Values.ingress.enabled -}} -{{- $fullName := include "bot-telegram.fullname" . -}} -{{- $svcPort := .Values.service.grpc.port -}} -{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} - {{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }} - {{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}} - {{- end }} -{{- end }} -{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}} -apiVersion: networking.k8s.io/v1 -{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} -apiVersion: networking.k8s.io/v1beta1 -{{- else -}} -apiVersion: extensions/v1beta1 -{{- end }} -kind: Ingress -metadata: - name: {{ $fullName }}-grpc - labels: - {{- include "bot-telegram.labels" . | nindent 4 }} - {{- with .Values.ingress.annotations }} - annotations: - {{- toYaml . | nindent 4 }} - {{- end }} -spec: - {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} - ingressClassName: {{ .Values.ingress.className }} - {{- end }} - {{- if .Values.ingress.tls }} - tls: - {{- range .Values.ingress.tls }} - - hosts: - {{- range .hosts }} - - {{ . | quote }} - {{- end }} - secretName: {{ .secretName }} - {{- end }} - {{- end }} - rules: - {{- range .Values.ingress.hosts }} - - host: {{ .host | quote }} - http: - paths: - {{- range .paths }} - - path: {{ .path }} - {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} - pathType: {{ .pathType }} - {{- end }} - backend: - {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} - service: - name: {{ $fullName }} - port: - number: {{ $svcPort }} - {{- else }} - serviceName: {{ $fullName }} - servicePort: {{ $svcPort }} - {{- end }} - {{- end }} - {{- end }} -{{- end }} diff --git a/helm/bot-telegram/templates/service.yaml b/helm/bot-telegram/templates/service.yaml index bbc9510..3a9b895 100644 --- a/helm/bot-telegram/templates/service.yaml +++ b/helm/bot-telegram/templates/service.yaml @@ -4,10 +4,7 @@ metadata: name: {{ include "bot-telegram.fullname" . }} labels: {{- include "bot-telegram.labels" . | nindent 4 }} - annotations: - prometheus.io/scrape: "true" - prometheus.io/path: "/metrics" - prometheus.io/port: "{{ .Values.service.metrics.port }}" + annotations: {} spec: type: {{ .Values.service.type }} ports: @@ -19,9 +16,9 @@ spec: targetPort: grpc protocol: TCP name: grpc - - port: {{ .Values.service.metrics.port }} - targetPort: metrics + - port: {{ .Values.service.callback.port }} + targetPort: callback protocol: TCP - name: metrics + name: callback selector: {{- include "bot-telegram.selectorLabels" . | nindent 4 }} diff --git a/helm/bot-telegram/values.yaml b/helm/bot-telegram/values.yaml index 7c10a25..750b683 100644 --- a/helm/bot-telegram/values.yaml +++ b/helm/bot-telegram/values.yaml @@ -2,7 +2,7 @@ # This is a YAML-formatted file. # Declare variables to be passed into your templates. -replicaCount: 2 +replicaCount: 1 image: repository: ghcr.io/awakari/bot-telegram @@ -40,8 +40,8 @@ service: port: 8080 grpc: port: 50051 - metrics: - port: 9090 + callback: + port: 8081 ingress: enabled: true @@ -91,6 +91,11 @@ api: uri: "api:56789" messages: uri: "messages:50051" + reader: + uri: "http://reader:8080/v1" + callback: + protocol: "http" + path: "/v1/chat" writer: uri: "resolver:50051" uri: "api:50051" @@ -110,26 +115,6 @@ cert: server: "https://acme-staging-v02.api.letsencrypt.org/directory" issuer: name: letsencrypt-staging -chats: - # Database related configuration. - db: - protocol: mongodb - hostname: "mongodb:27017" - # Database name to use. - name: bot-telegram - username: "root" - password: - raw: "" # used if limits.db.password.secret.enabled is false - secret: - enabled: true - name: "mongodb" - key: "mongodb-root-password" - table: - # Database table name to use. - name: chats - tls: - enabled: false - insecure: false log: # https://pkg.go.dev/golang.org/x/exp/slog#Level level: -4 diff --git a/main.go b/main.go index 05416de..af5eb44 100644 --- a/main.go +++ b/main.go @@ -1,13 +1,13 @@ package main import ( - "context" "crypto/tls" "fmt" grpcApi "github.com/awakari/bot-telegram/api/grpc" grpcApiAdmin "github.com/awakari/bot-telegram/api/grpc/admin" grpcApiMsgs "github.com/awakari/bot-telegram/api/grpc/messages" grpcApiTgBot "github.com/awakari/bot-telegram/api/grpc/tgbot" + "github.com/awakari/bot-telegram/api/http/reader" "github.com/awakari/bot-telegram/config" "github.com/awakari/bot-telegram/service" "github.com/awakari/bot-telegram/service/chats" @@ -18,25 +18,20 @@ import ( "github.com/awakari/client-sdk-go/api" "github.com/awakari/client-sdk-go/model" "github.com/cloudevents/sdk-go/binding/format/protobuf/v2/pb" + "github.com/gin-gonic/gin" "github.com/microcosm-cc/bluemonday" - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promhttp" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" "gopkg.in/telebot.v3" "log/slog" "net/http" "os" - "strconv" - "strings" "sync" "time" ) func main() { - ctx := context.TODO() - // init config and logger slog.Info("starting...") cfg, err := config.NewConfigFromEnv() @@ -48,19 +43,6 @@ func main() { } log := slog.New(slog.NewTextHandler(os.Stdout, &opts)) - // determine the replica index - replicaNameParts := strings.Split(cfg.Replica.Name, "-") - if len(replicaNameParts) < 2 { - panic("unable to parse the replica name: " + cfg.Replica.Name) - } - var replicaIndexTmp uint64 - replicaIndexTmp, err = strconv.ParseUint(replicaNameParts[len(replicaNameParts)-1], 10, 16) - if err != nil { - panic(err) - } - replicaIndex := uint32(replicaIndexTmp) - log.Info(fmt.Sprintf("Replica: %d/%d", replicaIndex, cfg.Replica.Range)) - // init Awakari client clientAwk, err := api. NewClientBuilder(). @@ -99,42 +81,18 @@ func main() { Build() defer clientAwkInternal.Close() - // init chat storage - var chatStor chats.Storage - chatStor, err = chats.NewStorage(ctx, cfg.Chats.Db) - if err != nil { - panic(err) - } - defer chatStor.Close() - // - prometheus.MustRegister( - prometheus.NewGaugeFunc( - prometheus.GaugeOpts{ - Name: "awk_bot_telegram_chats_total", - Help: "Awakari Telegram Bot: total active chat count", - }, - func() (v float64) { - count, err := chatStor.Count(context.TODO()) - if err != nil { - log.Error(fmt.Sprintf("Chat storage Count(): err=%s", err)) - } - return float64(count) - }, - ), - prometheus.NewGaugeFunc( - prometheus.GaugeOpts{ - Name: "awk_bot_telegram_subscribers_total", - Help: "Awakari Telegram Bot: total subscribers count", - }, - func() (v float64) { - count, err := chatStor.CountUsers(context.TODO()) - if err != nil { - log.Error(fmt.Sprintf("Chat storage CountUsers(): err=%s", err)) - } - return float64(count) - }, - ), + // init websub reader + clientHttp := http.Client{} + svcReader := reader.NewService(&clientHttp, cfg.Api.Reader.Uri) + svcReader = reader.NewServiceLogging(svcReader, log) + urlCallbackBase := fmt.Sprintf( + "%s://%s:%d/%s", + cfg.Api.Reader.CallBack.Protocol, + cfg.Api.Reader.CallBack.Host, + cfg.Api.Reader.CallBack.Port, + cfg.Api.Reader.CallBack.Path, ) + // init events format, see https://core.telegram.org/bots/api#html-style for details htmlPolicy := bluemonday.NewPolicy() htmlPolicy.AllowStandardURLs() @@ -153,7 +111,7 @@ func main() { AllowAttrs("class"). OnElements("code") htmlPolicy.AllowDataURIImages() - msgFmt := messages.Format{ + fmtMsg := messages.Format{ HtmlPolicy: htmlPolicy, } @@ -189,9 +147,9 @@ func main() { callbackHandlers := map[string]service.ArgHandlerFunc{ subscriptions.CmdDescription: subscriptions.DescriptionHandlerFunc(clientAwk, groupId), subscriptions.CmdExtend: subExtHandler.RequestExtensionDaysCount, - subscriptions.CmdStart: subscriptions.Start(log, clientAwk, chatStor, groupId, msgFmt), - subscriptions.CmdStop: subscriptions.Stop(chatStor), - subscriptions.CmdPageNext: subscriptions.PageNext(clientAwk, chatStor, groupId), + subscriptions.CmdStart: subscriptions.Start(clientAwk, svcReader, urlCallbackBase, groupId), + subscriptions.CmdStop: subscriptions.Stop(svcReader), + subscriptions.CmdPageNext: subscriptions.PageNext(clientAwk, svcReader, groupId), usage.CmdExtend: limitsHandler.RequestExtension, usage.CmdIncrease: limitsHandler.RequestIncrease, } @@ -290,11 +248,12 @@ func main() { controllerGrpc := grpcApiTgBot.NewController( []byte(cfg.Api.Telegram.Token), chanPostHandler, - chatStor, + svcReader, + urlCallbackBase, log, clientAwk, b, - msgFmt, + fmtMsg, ) go func() { log.Info(fmt.Sprintf("starting to listen the grpc API @ port #%d...", cfg.Api.Telegram.Bot.Port)) @@ -319,7 +278,7 @@ func main() { b.Use(func(next telebot.HandlerFunc) telebot.HandlerFunc { return service.LoggingHandlerFunc(next, log) }) - subListHandlerFunc := subscriptions.ListOnGroupStartHandlerFunc(clientAwk, chatStor, groupId) + subListHandlerFunc := subscriptions.ListOnGroupStartHandlerFunc(clientAwk, svcReader, groupId) b.Handle( "/start", service.ErrorHandlerFunc(func(tgCtx telebot.Context) (err error) { @@ -392,18 +351,18 @@ func main() { log.Warn(fmt.Sprintf("Failed to forward or pin the donation invoice in the chat %+v, cause: %s", chat, err)) return service.ErrorHandlerFunc(subListHandlerFunc)(tgCtx) }) - b.Handle(telebot.OnUserLeft, service.ErrorHandlerFunc(chats.UserLeftHandlerFunc(chatStor))) - - go func() { - var count uint32 - count, err = chats.ResumeAllReaders(ctx, log, chatStor, b, clientAwk, msgFmt, replicaIndex, cfg.Replica.Range) - log.Info(fmt.Sprintf("Resumed %d readers, errors: %s", count, err)) - }() - - go func() { - http.Handle("/metrics", promhttp.Handler()) - http.ListenAndServe(fmt.Sprintf(":%d", cfg.Api.Metrics.Port), nil) - }() + // + go b.Start() - b.Start() + // chats websub handler (subscriber) + hChats := chats.NewHandler(cfg.Api.Reader.Uri, fmtMsg, urlCallbackBase, svcReader, b) + r := gin.Default() + r. + Group(cfg.Api.Reader.CallBack.Path). + GET("/:chatId", hChats.Confirm). + POST("/:chatId", hChats.DeliverMessages) + err = r.Run(fmt.Sprintf(":%d", cfg.Api.Reader.CallBack.Port)) + if err != nil { + panic(err) + } } diff --git a/scripts/cover.sh b/scripts/cover.sh index 347865a..4e745cd 100755 --- a/scripts/cover.sh +++ b/scripts/cover.sh @@ -1,7 +1,7 @@ #!/bin/bash COVERAGE=$(cat cover.tmp) -THRESHOLD=8 +THRESHOLD=5 if [[ ${COVERAGE} -lt ${THRESHOLD} ]]; \ then \ echo "FAILED: test coverage ${COVERAGE}% < ${THRESHOLD}%"; \ diff --git a/service/chats/handler.go b/service/chats/handler.go new file mode 100644 index 0000000..36b31bd --- /dev/null +++ b/service/chats/handler.go @@ -0,0 +1,178 @@ +package chats + +import ( + "context" + "encoding/json" + "errors" + "fmt" + apiHttpReader "github.com/awakari/bot-telegram/api/http/reader" + "github.com/awakari/bot-telegram/service/messages" + ceProto "github.com/cloudevents/sdk-go/binding/format/protobuf/v2" + "github.com/cloudevents/sdk-go/binding/format/protobuf/v2/pb" + ce "github.com/cloudevents/sdk-go/v2/event" + "github.com/gin-gonic/gin" + "gopkg.in/telebot.v3" + "net/http" + "reflect" + "strconv" + "strings" +) + +type Handler interface { + Confirm(ctx *gin.Context) + DeliverMessages(ctx *gin.Context) +} + +type handler struct { + topicPrefixBase string + format messages.Format + urlCallbackBase string + svcReader apiHttpReader.Service + tgBot *telebot.Bot +} + +const keyHubChallenge = "hub.challenge" +const keyHubTopic = "hub.topic" +const linkSelfSuffix = ">; rel=\"self\"" +const keyAckCount = "X-Ack-Count" + +func NewHandler( + topicPrefixBase string, + format messages.Format, + urlCallbackBase string, + svcReader apiHttpReader.Service, + tgBot *telebot.Bot, +) Handler { + return handler{ + topicPrefixBase: topicPrefixBase, + format: format, + urlCallbackBase: urlCallbackBase, + svcReader: svcReader, + tgBot: tgBot, + } +} + +func (h handler) Confirm(ctx *gin.Context) { + topic := ctx.Query(keyHubTopic) + challenge := ctx.Query(keyHubChallenge) + if strings.HasSuffix(topic, h.topicPrefixBase+"/"+apiHttpReader.FmtJson) { + ctx.String(http.StatusOK, challenge) + } else { + ctx.String(http.StatusBadRequest, fmt.Sprintf("invalid topic: %s", topic)) + } + return +} + +func (h handler) DeliverMessages(ctx *gin.Context) { + + var topic string + links := ctx.Request.Header["link"] + for _, l := range links { + if strings.HasSuffix(l, linkSelfSuffix) && len(l) > len(linkSelfSuffix) { + topic = l[1 : len(l)-len(linkSelfSuffix)] + } + } + if topic == "" { + ctx.String(http.StatusBadRequest, "self link header missing in the request") + return + } + + var subId string + topicParts := strings.Split(topic, "/") + topicPartsLen := len(topicParts) + if topicPartsLen > 0 { + subId = topicParts[topicPartsLen-1] + } + if subId == "" { + ctx.String(http.StatusBadRequest, fmt.Sprintf("invalid self link header value in the request: %s", topic)) + return + } + + chatIdRaw := ctx.Param("chatId") + if chatIdRaw == "" { + ctx.String(http.StatusBadRequest, "chat id parameter is missing in the request URL") + return + } + chatId, err := strconv.ParseInt(chatIdRaw, 10, 64) + if err != nil { + ctx.String(http.StatusBadRequest, fmt.Sprintf("chat id parameter is not a valid integer: %s", chatIdRaw)) + return + } + + defer ctx.Request.Body.Close() + var evts []*ce.Event + err = json.NewDecoder(ctx.Request.Body).Decode(&evts) + if err != nil { + ctx.String(http.StatusBadRequest, fmt.Sprintf("failed to deserialize the request payload: %s", err)) + return + } + + var countAck uint32 + countAck, err = h.deliver(ctx, evts, subId, chatId) + if err == nil || countAck > 0 { + ctx.Writer.Header().Add(keyAckCount, strconv.FormatUint(uint64(countAck), 10)) + ctx.Status(http.StatusOK) + } else { + ctx.String(http.StatusInternalServerError, err.Error()) + } + + return +} + +func (h handler) deliver(ctx context.Context, evts []*ce.Event, subId string, chatId int64) (countAck uint32, err error) { + tgCtx := h.tgBot.NewContext(telebot.Update{ + Message: &telebot.Message{ + Chat: &telebot.Chat{ + ID: chatId, + }, + }, + }) + for _, evt := range evts { + var evtProto *pb.CloudEvent + evtProto, err = ceProto.ToProto(evt) + if err != nil { + break + } + tgMsg := h.format.Convert(evtProto, subId, messages.FormatModeHtml) + err = tgCtx.Send(tgMsg, telebot.ModeHTML) + if err != nil { + switch err.(type) { + case telebot.FloodError: + default: + errTb := &telebot.Error{} + if errors.As(err, &errTb) && errTb.Code == 403 { + fmt.Printf("Bot blocked: %s, removing the chat from the storage", err) + urlCallback := apiHttpReader.MakeCallbackUrl(h.urlCallbackBase, chatId) + err = h.svcReader.DeleteCallback(ctx, subId, urlCallback) + return + } + fmt.Printf("Failed to send message %+v to chat %d in HTML mode, cause: %s (%s)\n", tgMsg, chatId, err, reflect.TypeOf(err)) + tgMsg = h.format.Convert(evtProto, subId, messages.FormatModePlain) + err = tgCtx.Send(tgMsg) // fallback: try to re-send as a plain text + } + } + if err != nil { + switch err.(type) { + case telebot.FloodError: + default: + fmt.Printf("Failed to send message %+v in plain text mode, cause: %s\n", tgMsg, err) + tgMsg = h.format.Convert(evtProto, subId, messages.FormatModeRaw) + err = tgCtx.Send(tgMsg) // fallback: try to re-send as a raw text w/o file attachments + } + } + // + if err == nil { + countAck++ + } + if err != nil { + switch err.(type) { + case telebot.FloodError: + default: + fmt.Printf("FATAL: failed to send message %+v in raw text mode, cause: %s\n", tgMsg, err) + countAck++ // to skip + } + break + } + } + return +} diff --git a/service/chats/reader.go b/service/chats/reader.go deleted file mode 100644 index 9faadbb..0000000 --- a/service/chats/reader.go +++ /dev/null @@ -1,318 +0,0 @@ -package chats - -import ( - "context" - "errors" - "fmt" - "github.com/awakari/bot-telegram/service" - "github.com/awakari/bot-telegram/service/messages" - "github.com/awakari/client-sdk-go/api" - clientAwkApiReader "github.com/awakari/client-sdk-go/api/grpc/reader" - "github.com/awakari/client-sdk-go/api/grpc/subscriptions" - "github.com/awakari/client-sdk-go/model" - "github.com/awakari/client-sdk-go/model/subscription" - "github.com/cenkalti/backoff/v4" - "github.com/cloudevents/sdk-go/binding/format/protobuf/v2/pb" - "go.uber.org/ratelimit" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/metadata" - "google.golang.org/grpc/status" - "gopkg.in/telebot.v3" - "log/slog" - "reflect" - "sync" - "time" -) - -type Reader interface { - Run(ctx context.Context, log *slog.Logger) -} - -const ReaderTtl = 24 * time.Hour -const readBatchSize = 16 -const msgFmtReadOnceFailed = "unexpected failure: %s, retrying in %s..." -const backOffInit = 1 * time.Second -const backOffFactor = 3 -const backOffMax = 24 * time.Hour -const msgExpired = "⚠ The subscription has been expired." -const msgExpiresSoon = "⏳ The subscription expires in %s." -const msgFmtExtendSteps = " Please extend it." -const resumeBatchSize = 16 -const minIntervalLimit = 1 * time.Second -const day = 24 * time.Hour - -var runtimeReaders = make(map[string]*reader) -var runtimeReadersLock = &sync.Mutex{} - -func ResumeAllReaders( - ctx context.Context, - log *slog.Logger, - chatStor Storage, - tgBot *telebot.Bot, - clientAwk api.Client, - format messages.Format, - replicaIndex uint32, - replicaRange uint32, -) (count uint32, err error) { - cursor := "" - var page []Chat - for { - page, err = chatStor.GetBatch(ctx, replicaIndex, replicaRange, resumeBatchSize, cursor) - log.Debug(fmt.Sprintf("service.chats.GetBatch(%d, %d, %d, %s): %d, %s", replicaIndex, replicaRange, resumeBatchSize, cursor, len(page), err)) - if err != nil || len(page) == 0 { - break - } - cursor = page[len(page)-1].SubId - for _, c := range page { - u := telebot.Update{ - Message: &telebot.Message{ - Chat: &telebot.Chat{ - ID: c.Id, - }, - }, - } - r := NewReader(tgBot.NewContext(u), clientAwk, chatStor, c.Id, c.SubId, c.GroupId, c.UserId, format, c.MinInterval) - go r.Run(context.Background(), log) - count++ - } - } - return -} - -func StopChatReaders(chatId int64) { - runtimeReadersLock.Lock() - defer runtimeReadersLock.Unlock() - for _, r := range runtimeReaders { - if r.chatId == chatId { - r.stop = true - } - } -} - -func StopChatReader(subId string) (found bool) { - runtimeReadersLock.Lock() - defer runtimeReadersLock.Unlock() - var r *reader - r, found = runtimeReaders[subId] - if found { - r.stop = true - } - return -} - -func NewReader( - tgCtx telebot.Context, - clientAwk api.Client, - chatStor Storage, - chatId int64, - subId, groupId, userId string, - format messages.Format, - minInterval time.Duration, -) Reader { - if minInterval < minIntervalLimit { - minInterval = minIntervalLimit - } - maxRate := int(day / minInterval) - rl := ratelimit.New(maxRate, ratelimit.Per(day)) - return &reader{ - tgCtx: tgCtx, - clientAwk: clientAwk, - chatStor: chatStor, - chatId: chatId, - subId: subId, - groupId: groupId, - userId: userId, - format: format, - rl: rl, - } -} - -type reader struct { - tgCtx telebot.Context - clientAwk api.Client - chatStor Storage - chatId int64 - subId string - groupId string - userId string - stop bool - format messages.Format - rl ratelimit.Limiter -} - -func (r *reader) Run(ctx context.Context, log *slog.Logger) { - // - r.runtimeRegister(ctx) - defer r.runtimeUnregister(ctx) - // - var err error - for err == nil && !r.stop { - b := backoff.NewExponentialBackOff() - b.InitialInterval = backOffInit - b.Multiplier = backOffFactor - b.MaxInterval, b.MaxElapsedTime = backOffMax, backOffMax - err = backoff.RetryNotify(r.runOnce, b, func(err error, d time.Duration) { - log.Warn(fmt.Sprintf(msgFmtReadOnceFailed, err, d)) - }) - if errors.Is(err, context.DeadlineExceeded) { - err = nil - } - } - // - if err != nil { - panic(err) - } -} - -func (r *reader) runOnce() (err error) { - // prepare the context with a certain timeout - ctx := context.Background() - groupIdCtx := metadata.AppendToOutgoingContext(ctx, service.KeyGroupId, r.groupId) - r.checkExpiration(groupIdCtx) - groupIdCtx, cancel := context.WithTimeout(groupIdCtx, ReaderTtl) - defer cancel() - // get subscription info - var sd subscription.Data - sd, err = r.clientAwk.ReadSubscription(groupIdCtx, r.userId, r.subId) - // open the events reader - var readerAwk model.AckReader[[]*pb.CloudEvent] - var subDescr string - if err == nil { - subDescr = sd.Description - readerAwk, err = r.clientAwk.OpenMessagesAckReader(groupIdCtx, r.userId, r.subId, readBatchSize) - } - if err == nil { - defer readerAwk.Close() - err = r.deliverEventsReadLoop(ctx, readerAwk, r.subId, subDescr) - } - switch { - case errors.Is(err, subscriptions.ErrNotFound): - fallthrough - case errors.Is(err, api.ErrApiDisabled): - fallthrough - case errors.Is(err, clientAwkApiReader.ErrNotFound): - _ = r.tgCtx.Send(fmt.Sprintf("failed to read by subscription: %s, cause: %s, stopping", err, r.subId)) - _ = r.chatStor.UnlinkSubscription(ctx, r.subId) - r.stop = true - err = nil - } - return -} - -func (r *reader) checkExpiration(groupIdCtx context.Context) { - sd, err := r.clientAwk.ReadSubscription(groupIdCtx, r.userId, r.subId) - if err == nil { - switch { - case sd.Expires.IsZero(): // never expires - case sd.Expires.Before(time.Now().UTC()): - _ = r.tgCtx.Send(msgExpired + msgFmtExtendSteps) - case sd.Expires.Sub(time.Now().UTC()) < 168*time.Hour: // expires earlier than in 1 week - _ = r.tgCtx.Send(fmt.Sprintf(msgExpiresSoon, sd.Expires.Sub(time.Now().UTC()).Round(time.Minute)) + msgFmtExtendSteps) - } - } -} - -func (r *reader) deliverEventsReadLoop( - ctx context.Context, - readerAwk model.AckReader[[]*pb.CloudEvent], - subId, subDescr string, -) (err error) { - for !r.stop { - err = r.deliverEventsRead(ctx, readerAwk, subId, subDescr) - if err != nil { - break - } - } - return -} - -func (r *reader) deliverEventsRead( - ctx context.Context, - readerAwk model.AckReader[[]*pb.CloudEvent], - subId, subDescr string, -) (err error) { - var evts []*pb.CloudEvent - evts, err = readerAwk.Read() - switch status.Code(err) { - case codes.OK: - var countAck uint32 - if len(evts) > 0 { - countAck, err = r.deliverEvents(evts, subId, subDescr) - } - _ = readerAwk.Ack(countAck) - if err != nil { - switch err.(type) { - case telebot.FloodError: - d := time.Second * time.Duration(err.(telebot.FloodError).RetryAfter) - fmt.Printf("Flood error, retry in %s\n", d) - time.Sleep(d) - err = nil - } - } - case codes.NotFound: - _ = r.tgCtx.Send(fmt.Sprintf("subscription %s doesn't exist, stopping", r.subId)) - _ = r.chatStor.UnlinkSubscription(ctx, r.subId) - r.stop = true - err = nil - } - return err -} - -func (r *reader) deliverEvents(evts []*pb.CloudEvent, subId, subDescr string) (countAck uint32, err error) { - for _, evt := range evts { - r.rl.Take() - tgMsg := r.format.Convert(evt, subId, subDescr, messages.FormatModeHtml) - err = r.tgCtx.Send(tgMsg, telebot.ModeHTML) - if err != nil { - switch err.(type) { - case telebot.FloodError: - default: - errTb := &telebot.Error{} - if errors.As(err, &errTb) && errTb.Code == 403 { - fmt.Printf("Bot blocked: %s, removing the chat from the storage", err) - _, _ = r.chatStor.Delete(context.TODO(), r.tgCtx.Chat().ID) - r.stop = true - return - } - fmt.Printf("Failed to send message %+v to chat %d in HTML mode, cause: %s (%s)\n", tgMsg, r.tgCtx.Chat().ID, err, reflect.TypeOf(err)) - tgMsg = r.format.Convert(evt, subId, subDescr, messages.FormatModePlain) - err = r.tgCtx.Send(tgMsg) // fallback: try to re-send as a plain text - } - } - if err != nil { - switch err.(type) { - case telebot.FloodError: - default: - fmt.Printf("Failed to send message %+v in plain text mode, cause: %s\n", tgMsg, err) - tgMsg = r.format.Convert(evt, subId, subDescr, messages.FormatModeRaw) - err = r.tgCtx.Send(tgMsg) // fallback: try to re-send as a raw text w/o file attachments - } - } - // - if err == nil { - countAck++ - } - if err != nil { - switch err.(type) { - case telebot.FloodError: - default: - fmt.Printf("FATAL: failed to send message %+v in raw text mode, cause: %s\n", tgMsg, err) - countAck++ // to skip - } - break - } - } - return -} - -func (r *reader) runtimeRegister(_ context.Context) { - runtimeReadersLock.Lock() - defer runtimeReadersLock.Unlock() - runtimeReaders[r.subId] = r -} - -func (r *reader) runtimeUnregister(ctx context.Context) { - runtimeReadersLock.Lock() - defer runtimeReadersLock.Unlock() - delete(runtimeReaders, r.subId) -} diff --git a/service/chats/storage.go b/service/chats/storage.go deleted file mode 100644 index 4ba561e..0000000 --- a/service/chats/storage.go +++ /dev/null @@ -1,17 +0,0 @@ -package chats - -import ( - "context" - "io" -) - -type Storage interface { - io.Closer - LinkSubscription(ctx context.Context, c Chat) (err error) - GetSubscriptionLink(ctx context.Context, subId string) (c Chat, err error) - UnlinkSubscription(ctx context.Context, subId string) (err error) - Delete(ctx context.Context, id int64) (count int64, err error) - GetBatch(ctx context.Context, idRem, idDiv uint32, limit uint32, cursor string) (page []Chat, err error) - Count(ctx context.Context) (count int64, err error) - CountUsers(ctx context.Context) (count int64, err error) -} diff --git a/service/chats/storage_mongo.go b/service/chats/storage_mongo.go deleted file mode 100644 index ba1d605..0000000 --- a/service/chats/storage_mongo.go +++ /dev/null @@ -1,286 +0,0 @@ -package chats - -import ( - "context" - "crypto/tls" - "errors" - "fmt" - "github.com/awakari/bot-telegram/config" - "go.mongodb.org/mongo-driver/bson" - "go.mongodb.org/mongo-driver/mongo" - "go.mongodb.org/mongo-driver/mongo/options" - "time" -) - -type storageMongo struct { - conn *mongo.Client - db *mongo.Database - coll *mongo.Collection -} - -type Chat struct { - Id int64 `bson:"id"` - SubId string `bson:"subId"` - GroupId string `bson:"groupId"` - UserId string `bson:"userId"` - MinInterval time.Duration `bson:"minInterval"` -} - -const attrId = "id" -const attrSubId = "subId" -const attrGroupId = "groupId" -const attrUserId = "userId" - -const keyCountUsers = "countUsers" - -var optsSrvApi = options.ServerAPI(options.ServerAPIVersion1) -var indices = []mongo.IndexModel{ - { - Keys: bson.D{ - { - Key: attrId, - Value: 1, - }, - }, - Options: options. - Index(). - SetUnique(false), - }, - { - Keys: bson.D{ - { - Key: attrSubId, - Value: 1, - }, - }, - Options: options. - Index(). - SetUnique(true), - }, -} -var projGet = bson.D{ - { - Key: attrId, - Value: 1, - }, - { - Key: attrGroupId, - Value: 1, - }, - { - Key: attrUserId, - Value: 1, - }, -} -var optsGet = options. - FindOne(). - SetShowRecordID(false). - SetProjection(projGet) -var sortGetBatch = bson.D{ - { - Key: attrSubId, - Value: 1, - }, -} -var projGetBatch = bson.D{ - { - Key: attrId, - Value: 1, - }, - { - Key: attrSubId, - Value: 1, - }, - { - Key: attrGroupId, - Value: 1, - }, - { - Key: attrUserId, - Value: 1, - }, -} - -var pipelineCountUsers = mongo.Pipeline{ - bson.D{{ - "$group", - bson.D{{ - "_id", - "$" + attrUserId, - }}, - }}, - bson.D{{ - "$count", - keyCountUsers, - }}, -} - -func NewStorage(ctx context.Context, cfgDb config.ChatsDbConfig) (s Storage, err error) { - clientOpts := options. - Client(). - ApplyURI(cfgDb.Uri). - SetServerAPIOptions(optsSrvApi) - if cfgDb.Tls.Enabled { - clientOpts = clientOpts.SetTLSConfig(&tls.Config{InsecureSkipVerify: cfgDb.Tls.Insecure}) - } - if len(cfgDb.UserName) > 0 { - auth := options.Credential{ - Username: cfgDb.UserName, - Password: cfgDb.Password, - PasswordSet: len(cfgDb.Password) > 0, - } - clientOpts = clientOpts.SetAuth(auth) - } - conn, err := mongo.Connect(ctx, clientOpts) - var sm storageMongo - if err == nil { - db := conn.Database(cfgDb.Name) - coll := db.Collection(cfgDb.Table.Name) - sm.conn = conn - sm.db = db - sm.coll = coll - _, err = sm.ensureIndices(ctx) - } - if err == nil { - s = sm - } - return -} - -func (sm storageMongo) ensureIndices(ctx context.Context) ([]string, error) { - return sm.coll.Indexes().CreateMany(ctx, indices) -} - -func (sm storageMongo) Close() error { - return sm.conn.Disconnect(context.TODO()) -} - -func (sm storageMongo) LinkSubscription(ctx context.Context, c Chat) (err error) { - _, err = sm.coll.InsertOne(ctx, c) - err = decodeMongoError(err) - return -} - -func (sm storageMongo) GetSubscriptionLink(ctx context.Context, subId string) (c Chat, err error) { - q := bson.M{ - attrSubId: subId, - } - var result *mongo.SingleResult - result = sm.coll.FindOne(ctx, q, optsGet) - err = result.Err() - if err == nil { - err = result.Decode(&c) - } - return -} - -func (sm storageMongo) UnlinkSubscription(ctx context.Context, subId string) (err error) { - q := bson.M{ - attrSubId: subId, - } - _, err = sm.coll.DeleteOne(ctx, q) - err = decodeMongoError(err) - return -} - -func (sm storageMongo) Delete(ctx context.Context, id int64) (count int64, err error) { - q := bson.M{ - attrId: id, - } - var result *mongo.DeleteResult - result, err = sm.coll.DeleteMany(ctx, q) - if result != nil { - count = result.DeletedCount - } - err = decodeMongoError(err) - return -} - -func (sm storageMongo) GetBatch(ctx context.Context, idRem, idDiv uint32, limit uint32, cursor string) (page []Chat, err error) { - q := bson.M{ - "$and": []bson.M{ - { - attrSubId: bson.M{ - "$gt": cursor, - }, - }, - { - "$or": []bson.M{ - { - attrId: bson.M{ - "$mod": bson.A{ - idDiv, - idRem, - }, - }, - }, - { - attrId: bson.M{ - "$mod": bson.A{ - idDiv, - -int32(idRem), - }, - }, - }, - }, - }, - }, - } - optsList := options. - Find(). - SetLimit(int64(limit)). - SetShowRecordID(false). - SetSort(sortGetBatch). - SetProjection(projGetBatch) - var cur *mongo.Cursor - cur, err = sm.coll.Find(ctx, q, optsList) - if err == nil { - for cur.Next(ctx) { - var rec Chat - err = errors.Join(err, cur.Decode(&rec)) - if err == nil { - page = append(page, rec) - } - } - } - err = decodeMongoError(err) - return -} - -func (sm storageMongo) Count(ctx context.Context) (count int64, err error) { - return sm.coll.EstimatedDocumentCount(ctx) -} - -func (s storageMongo) CountUsers(ctx context.Context) (count int64, err error) { - var cursor *mongo.Cursor - cursor, err = s.coll.Aggregate(ctx, pipelineCountUsers) - var result bson.M - if err == nil && cursor.Next(ctx) { - err = cursor.Decode(&result) - } - if err == nil { - rawCount := result[keyCountUsers] - switch rawCount.(type) { - case int32: - count = int64(rawCount.(int32)) - case int64: - count = rawCount.(int64) - default: - err = fmt.Errorf("%w: failed to convert result to int: %+v", ErrInternal, rawCount) - } - } - return -} - -func decodeMongoError(src error) (dst error) { - switch { - case src == nil: - case mongo.IsDuplicateKeyError(src): - dst = fmt.Errorf("%w: %s", ErrAlreadyExists, src) - case errors.Is(src, mongo.ErrNoDocuments): - dst = ErrNotFound - default: - dst = fmt.Errorf("%w: %s", ErrInternal, src) - } - return -} diff --git a/service/chats/storage_mongo_test.go b/service/chats/storage_mongo_test.go deleted file mode 100644 index d320d76..0000000 --- a/service/chats/storage_mongo_test.go +++ /dev/null @@ -1,334 +0,0 @@ -package chats - -import ( - "context" - "fmt" - "github.com/awakari/bot-telegram/config" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "os" - "testing" - "time" -) - -var dbUri = os.Getenv("DB_URI_TEST_MONGO") - -func TestNewStorage(t *testing.T) { - // - collName := fmt.Sprintf("chats-test-%d", time.Now().UnixMicro()) - dbCfg := config.ChatsDbConfig{ - Uri: dbUri, - Name: "bot-telegram", - } - dbCfg.Table.Name = collName - ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) - defer cancel() - s, err := NewStorage(ctx, dbCfg) - assert.NotNil(t, s) - assert.Nil(t, err) - // - clear(ctx, t, s.(storageMongo)) -} - -func clear(ctx context.Context, t *testing.T, sm storageMongo) { - require.Nil(t, sm.coll.Drop(ctx)) - require.Nil(t, sm.Close()) -} - -func TestStorageMongo_Create(t *testing.T) { - // - collName := fmt.Sprintf("chats-test-%d", time.Now().UnixMicro()) - dbCfg := config.ChatsDbConfig{ - Uri: dbUri, - Name: "bot-telegram", - } - dbCfg.Table.Name = collName - ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) - defer cancel() - s, err := NewStorage(ctx, dbCfg) - require.NotNil(t, s) - require.Nil(t, err) - sm := s.(storageMongo) - defer clear(ctx, t, sm) - // - preExisting := Chat{ - Id: -123, - SubId: "sub0", - } - err = s.LinkSubscription(ctx, preExisting) - require.Nil(t, err) - // - cases := map[string]struct { - chat Chat - err error - }{ - "ok": { - chat: Chat{ - Id: 234, - SubId: "sub1", - }, - }, - "already exists - same subscription": { - chat: Chat{ - Id: 345, - SubId: "sub0", - }, - err: ErrAlreadyExists, - }, - "already exists - same chat id": { - chat: Chat{ - Id: -123, - SubId: "sub1", - }, - err: ErrAlreadyExists, - }, - } - // - for k, c := range cases { - t.Run(k, func(t *testing.T) { - err = s.LinkSubscription(ctx, c.chat) - assert.ErrorIs(t, err, c.err) - }) - } -} - -func TestStorageMongo_Delete(t *testing.T) { - // - collName := fmt.Sprintf("chats-test-%d", time.Now().UnixMicro()) - dbCfg := config.ChatsDbConfig{ - Uri: dbUri, - Name: "bot-telegram", - } - dbCfg.Table.Name = collName - ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) - defer cancel() - s, err := NewStorage(ctx, dbCfg) - require.NotNil(t, s) - require.Nil(t, err) - sm := s.(storageMongo) - defer clear(ctx, t, sm) - // - preExisting0 := Chat{ - Id: -123, - SubId: "sub0", - } - err = s.LinkSubscription(ctx, preExisting0) - require.Nil(t, err) - // - preExisting1 := Chat{ - Id: -123, - SubId: "sub1", - } - err = s.LinkSubscription(ctx, preExisting1) - require.Nil(t, err) - // - cases := map[string]struct { - id int64 - count int64 - err error - }{ - "ok": { - id: -123, - count: 2, - }, - "not found is ok": { - id: 234, - }, - } - // - for k, c := range cases { - t.Run(k, func(t *testing.T) { - count, err := s.Delete(ctx, c.id) - assert.Equal(t, c.count, count) - assert.ErrorIs(t, err, c.err) - }) - } -} - -func TestStorageMongo_GetBatch(t *testing.T) { - // - collName := fmt.Sprintf("chats-test-%d", time.Now().UnixMicro()) - dbCfg := config.ChatsDbConfig{ - Uri: dbUri, - Name: "bot-telegram", - } - dbCfg.Table.Name = collName - ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) - defer cancel() - s, err := NewStorage(ctx, dbCfg) - require.NotNil(t, s) - require.Nil(t, err) - sm := s.(storageMongo) - defer clear(ctx, t, sm) - // - cases := map[string]struct { - idRange uint32 - idIndex uint32 - stored []Chat - selected []Chat - }{ - "ok": { - idRange: 2, - idIndex: 1, - stored: []Chat{ - { - Id: -1001875128866, - SubId: "sub1", - GroupId: "group1", - UserId: "user1", - }, - { - Id: -1001778619305, - SubId: "sub2", - GroupId: "group2", - UserId: "user2", - }, - { - Id: -1001733378662, - SubId: "sub3", - GroupId: "group3", - UserId: "user3", - }, - }, - selected: []Chat{ - { - Id: -1001778619305, - SubId: "sub2", - GroupId: "group2", - UserId: "user2", - }, - }, - }, - } - // - for k, c := range cases { - t.Run(k, func(t *testing.T) { - for _, chat := range c.stored { - err = s.LinkSubscription(ctx, chat) - require.Nil(t, err) - } - var selected []Chat - selected, err = s.GetBatch(ctx, c.idIndex, c.idRange, 10, "") - assert.Equal(t, c.selected, selected) - assert.Nil(t, err) - }) - } -} - -func TestStorageMongo_Count(t *testing.T) { - // - collName := fmt.Sprintf("chats-test-%d", time.Now().UnixMicro()) - dbCfg := config.ChatsDbConfig{ - Uri: dbUri, - Name: "bot-telegram", - } - dbCfg.Table.Name = collName - ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) - defer cancel() - s, err := NewStorage(ctx, dbCfg) - require.NotNil(t, s) - require.Nil(t, err) - sm := s.(storageMongo) - defer clear(ctx, t, sm) - // - cases := map[string]struct { - stored []Chat - out int64 - err error - }{ - "ok": { - stored: []Chat{ - { - Id: -1001875128866, - SubId: "sub1", - GroupId: "group1", - UserId: "user1", - }, - { - Id: -1001778619305, - SubId: "sub2", - GroupId: "group2", - UserId: "user2", - }, - { - Id: -1001733378662, - SubId: "sub3", - GroupId: "group3", - UserId: "user3", - }, - }, - out: 3, - }, - } - // - for k, c := range cases { - t.Run(k, func(t *testing.T) { - for _, chat := range c.stored { - err = s.LinkSubscription(ctx, chat) - require.Nil(t, err) - } - count, err := s.Count(ctx) - assert.Equal(t, c.out, count) - assert.Nil(t, err) - }) - } -} - -func TestStorageMongo_CountUsers(t *testing.T) { - // - collName := fmt.Sprintf("chats-test-%d", time.Now().UnixMicro()) - dbCfg := config.ChatsDbConfig{ - Uri: dbUri, - Name: "bot-telegram", - } - dbCfg.Table.Name = collName - ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) - defer cancel() - s, err := NewStorage(ctx, dbCfg) - require.NotNil(t, s) - require.Nil(t, err) - sm := s.(storageMongo) - defer clear(ctx, t, sm) - // - cases := map[string]struct { - stored []Chat - out int64 - err error - }{ - "ok": { - stored: []Chat{ - { - Id: -1001875128866, - SubId: "sub1", - GroupId: "group1", - UserId: "user1", - }, - { - Id: -1001778619305, - SubId: "sub2", - GroupId: "group1", - UserId: "user2", - }, - { - Id: -1001733378662, - SubId: "sub3", - GroupId: "group2", - UserId: "user2", - }, - }, - out: 2, - }, - } - // - for k, c := range cases { - t.Run(k, func(t *testing.T) { - for _, chat := range c.stored { - err = s.LinkSubscription(ctx, chat) - require.Nil(t, err) - } - count, err := s.CountUsers(ctx) - assert.Equal(t, c.out, count) - assert.Nil(t, err) - }) - } -} diff --git a/service/chats/user_left.go b/service/chats/user_left.go deleted file mode 100644 index c5fbfd6..0000000 --- a/service/chats/user_left.go +++ /dev/null @@ -1,22 +0,0 @@ -package chats - -import ( - "context" - "errors" - "gopkg.in/telebot.v3" -) - -func UserLeftHandlerFunc(chatStor Storage) telebot.HandlerFunc { - return func(ctx telebot.Context) (err error) { - chat := ctx.Chat() - if chat == nil { - err = errors.New("user left a missing chat") - } - if err == nil { - chatId := chat.ID - StopChatReaders(chatId) - _, _ = chatStor.Delete(context.Background(), chatId) - } - return - } -} diff --git a/service/messages/format.go b/service/messages/format.go index d66568f..def1b3b 100644 --- a/service/messages/format.go +++ b/service/messages/format.go @@ -30,7 +30,7 @@ var htmlStripTags = bluemonday. StrictPolicy(). AddSpaceWhenStrippingTag(true) -func (f Format) Convert(evt *pb.CloudEvent, subId, subDescr string, mode FormatMode) (tgMsg any) { +func (f Format) Convert(evt *pb.CloudEvent, subId string, mode FormatMode) (tgMsg any) { fileTypeAttr, fileTypeFound := evt.Attributes[attrKeyFileType] if fileTypeFound && mode != FormatModeRaw { ft := FileType(fileTypeAttr.GetCeInteger()) @@ -43,19 +43,19 @@ func (f Format) Convert(evt *pb.CloudEvent, subId, subDescr string, mode FormatM tgMsg = &telebot.Audio{ File: file, Duration: int(evt.Attributes[attrKeyFileMediaDuration].GetCeInteger()), - Caption: f.convert(evt, subId, subDescr, mode, false, false), + Caption: f.convert(evt, subId, mode, false, false), } case FileTypeDocument: tgMsg = &telebot.Document{ File: file, - Caption: f.convert(evt, subId, subDescr, mode, false, false), + Caption: f.convert(evt, subId, mode, false, false), } case FileTypeImage: tgMsg = &telebot.Photo{ File: file, Width: int(evt.Attributes[attrKeyFileImgWidth].GetCeInteger()), Height: int(evt.Attributes[attrKeyFileImgHeight].GetCeInteger()), - Caption: f.convert(evt, subId, subDescr, mode, false, false), + Caption: f.convert(evt, subId, mode, false, false), } case FileTypeVideo: tgMsg = &telebot.Video{ @@ -63,7 +63,7 @@ func (f Format) Convert(evt *pb.CloudEvent, subId, subDescr string, mode FormatM Width: int(evt.Attributes[attrKeyFileImgWidth].GetCeInteger()), Height: int(evt.Attributes[attrKeyFileImgHeight].GetCeInteger()), Duration: int(evt.Attributes[attrKeyFileMediaDuration].GetCeInteger()), - Caption: f.convert(evt, subId, subDescr, mode, false, false), + Caption: f.convert(evt, subId, mode, false, false), } } } else { @@ -72,15 +72,15 @@ func (f Format) Convert(evt *pb.CloudEvent, subId, subDescr string, mode FormatM case true: // no need to truncate for telegram when message is from telegram // no need to convert any other attributes except text and footer - tgMsg = f.convert(evt, subId, subDescr, mode, false, true) + tgMsg = f.convert(evt, subId, mode, false, true) default: - tgMsg = f.convert(evt, subId, subDescr, mode, true, true) + tgMsg = f.convert(evt, subId, mode, true, true) } } return } -func (f Format) convert(evt *pb.CloudEvent, subId, subDescr string, mode FormatMode, trunc, attrs bool) (txt string) { +func (f Format) convert(evt *pb.CloudEvent, subId string, mode FormatMode, trunc, attrs bool) (txt string) { if attrs { txt += f.convertHeaderAttrs(evt, mode, trunc) } @@ -145,10 +145,12 @@ func (f Format) convert(evt *pb.CloudEvent, subId, subDescr string, mode FormatM } // subDetailsLink := "https://awakari.com/sub-details.html?id=" + subId - if mode == FormatModeHtml { - subDetailsLink = "" + subDescr + "" + switch mode { + case FormatModeHtml: + txt += "Subscription\n\n" + default: + txt += "Subscription: " + subDetailsLink + "\n\n" } - txt += "Subscription: " + subDetailsLink + "\n\n" // var attrsTxt string if attrs { diff --git a/service/subscriptions/list.go b/service/subscriptions/list.go index f5bfea3..81faea3 100644 --- a/service/subscriptions/list.go +++ b/service/subscriptions/list.go @@ -3,8 +3,8 @@ package subscriptions import ( "context" "fmt" + "github.com/awakari/bot-telegram/api/http/reader" "github.com/awakari/bot-telegram/service" - "github.com/awakari/bot-telegram/service/chats" "github.com/awakari/client-sdk-go/api" "github.com/awakari/client-sdk-go/model/subscription" "google.golang.org/grpc/metadata" @@ -13,12 +13,12 @@ import ( const CmdPageNext = "subs_next" -func ListOnGroupStartHandlerFunc(clientAwk api.Client, chatStor chats.Storage, groupId string) telebot.HandlerFunc { +func ListOnGroupStartHandlerFunc(clientAwk api.Client, svcReader reader.Service, groupId string) telebot.HandlerFunc { return func(tgCtx telebot.Context) (err error) { groupIdCtx := metadata.AppendToOutgoingContext(context.TODO(), service.KeyGroupId, groupId) userId := fmt.Sprintf(service.FmtUserId, tgCtx.Sender().ID) var m *telebot.ReplyMarkup - m, err = listButtons(groupIdCtx, userId, clientAwk, chatStor, tgCtx.Chat().ID, CmdStart, "") + m, err = listButtons(groupIdCtx, userId, clientAwk, svcReader, tgCtx.Chat().ID, CmdStart, "") if err == nil { err = tgCtx.Send( "Own subscriptions list. "+ @@ -31,7 +31,7 @@ func ListOnGroupStartHandlerFunc(clientAwk api.Client, chatStor chats.Storage, g } } -func PageNext(clientAwk api.Client, chatStor chats.Storage, groupId string) service.ArgHandlerFunc { +func PageNext(clientAwk api.Client, svcReader reader.Service, groupId string) service.ArgHandlerFunc { return func(tgCtx telebot.Context, args ...string) (err error) { groupIdCtx := metadata.AppendToOutgoingContext(context.TODO(), service.KeyGroupId, groupId) userId := fmt.Sprintf(service.FmtUserId, tgCtx.Sender().ID) @@ -40,7 +40,7 @@ func PageNext(clientAwk api.Client, chatStor chats.Storage, groupId string) serv cursor = args[1] } var m *telebot.ReplyMarkup - m, err = listButtons(groupIdCtx, userId, clientAwk, chatStor, tgCtx.Chat().ID, args[0], cursor) + m, err = listButtons(groupIdCtx, userId, clientAwk, svcReader, tgCtx.Chat().ID, args[0], cursor) if err == nil { err = tgCtx.Send("Own subscriptions list page:", m, telebot.ModeHTML) } @@ -52,7 +52,7 @@ func listButtons( groupIdCtx context.Context, userId string, clientAwk api.Client, - chatStor chats.Storage, + svcReader reader.Service, chatId int64, btnCmd string, cursor string, @@ -68,11 +68,13 @@ func listButtons( var subLinked bool var subLinkedHere bool if err == nil { - var c chats.Chat - c, err = chatStor.GetSubscriptionLink(groupIdCtx, subId) + var cb reader.Callback + cb, err = svcReader.GetCallback(groupIdCtx, subId) if err == nil { subLinked = true - if c.Id == chatId { + var cbChatId int64 + cbChatId, err = reader.GetCallbackUrlChatId(cb.Url) + if err == nil && cbChatId == chatId { subLinkedHere = true } } diff --git a/service/subscriptions/start.go b/service/subscriptions/start.go index 630b81b..bf665da 100644 --- a/service/subscriptions/start.go +++ b/service/subscriptions/start.go @@ -4,14 +4,13 @@ import ( "context" "errors" "fmt" + "github.com/awakari/bot-telegram/api/http/reader" "github.com/awakari/bot-telegram/service" "github.com/awakari/bot-telegram/service/chats" - "github.com/awakari/bot-telegram/service/messages" "github.com/awakari/client-sdk-go/api" "github.com/awakari/client-sdk-go/model/subscription" "google.golang.org/grpc/metadata" "gopkg.in/telebot.v3" - "log/slog" "time" ) @@ -30,11 +29,10 @@ var deliveryIntervalRows = [][]string{ } func Start( - log *slog.Logger, clientAwk api.Client, - chatStor chats.Storage, + svcReader reader.Service, + urlCallbackBase string, groupId string, - msgFmt messages.Format, ) service.ArgHandlerFunc { return func(tgCtx telebot.Context, args ...string) (err error) { switch len(args) { @@ -48,7 +46,7 @@ func Start( minInterval, err = time.ParseDuration(minIntervalStr) switch err { case nil: - err = start(tgCtx, log, clientAwk, chatStor, subId, groupId, msgFmt, minInterval) + err = start(tgCtx, clientAwk, svcReader, urlCallbackBase, subId, groupId, minInterval) default: err = errors.New(fmt.Sprintf("failed to parse min delivery interval: %s", err)) } @@ -81,31 +79,24 @@ func requestDeliveryInterval(tgCtx telebot.Context, subId string) (err error) { func start( tgCtx telebot.Context, - log *slog.Logger, clientAwk api.Client, - chatStor chats.Storage, - subId, groupId string, - msgFmt messages.Format, + svcReader reader.Service, + urlCallbackBase string, + subId string, + groupId string, minInterval time.Duration, ) (err error) { - var userId string - var chat chats.Chat - if err == nil { - userId = fmt.Sprintf(service.FmtUserId, tgCtx.Sender().ID) - chat.Id = tgCtx.Chat().ID - } + ctx := context.TODO() + userId := fmt.Sprintf(service.FmtUserId, tgCtx.Sender().ID) + urlCallback := reader.MakeCallbackUrl(urlCallbackBase, tgCtx.Chat().ID) if err == nil { - chat.SubId = subId - chat.GroupId = groupId - chat.UserId = userId - chat.MinInterval = minInterval - err = chatStor.LinkSubscription(context.TODO(), chat) + err = svcReader.CreateCallback(ctx, subId, urlCallback) switch { case errors.Is(err, chats.ErrAlreadyExists): // might be not an error, so try to re-link the subscription - err = chatStor.UnlinkSubscription(context.TODO(), subId) + err = svcReader.DeleteCallback(ctx, subId, urlCallback) if err == nil { - err = chatStor.LinkSubscription(context.TODO(), chat) + err = svcReader.CreateCallback(ctx, subId, urlCallback) } } } @@ -117,9 +108,5 @@ func start( if err == nil { err = tgCtx.Send(fmt.Sprintf(MsgFmtChatLinked, subData.Description, minInterval), telebot.ModeHTML, telebot.NoPreview) } - if err == nil { - r := chats.NewReader(tgCtx, clientAwk, chatStor, chat.Id, chat.SubId, groupId, userId, msgFmt, minInterval) - go r.Run(context.Background(), log) - } return } diff --git a/service/subscriptions/stop.go b/service/subscriptions/stop.go index 52fb025..f4f334d 100644 --- a/service/subscriptions/stop.go +++ b/service/subscriptions/stop.go @@ -2,24 +2,24 @@ package subscriptions import ( "context" - "fmt" + "github.com/awakari/bot-telegram/api/http/reader" "github.com/awakari/bot-telegram/service" - "github.com/awakari/bot-telegram/service/chats" "gopkg.in/telebot.v3" ) const CmdStop = "sub_stop" -func Stop(chatStor chats.Storage) service.ArgHandlerFunc { +func Stop(svcReader reader.Service) service.ArgHandlerFunc { return func(tgCtx telebot.Context, args ...string) (err error) { + ctx := context.TODO() subId := args[0] - err = chatStor.UnlinkSubscription(context.Background(), subId) + var cb reader.Callback + cb, err = svcReader.GetCallback(ctx, subId) if err == nil { - if chats.StopChatReader(subId) { - _ = tgCtx.Send("Unlinked the subscription from this chat") - } else { - _ = tgCtx.Send(fmt.Sprintf("Unlinked the subscription from this chat. Note: don't delete this group for the next %s. Some new messages may appear here.", chats.ReaderTtl)) - } + err = svcReader.DeleteCallback(ctx, subId, cb.Url) + } + if err == nil { + _ = tgCtx.Send("Unlinked the subscription from this chat") } return }