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
}