Files

91 lines
2.9 KiB
Go

package telegram
import (
"io"
"log/slog"
"net/http"
"time"
"git.yeij.top/AskaEth/Cyrene_v2/internal/channel/adapter"
"git.yeij.top/AskaEth/Cyrene_v2/internal/logger"
)
// Defaults and knob values for the Telegram Bot API integration.
const (
// defaultBaseURL is the Telegram API root for unconfigured adapters.
defaultBaseURL = "https://api.telegram.org"
// httpTimeout bounds a single Bot API request.
httpTimeout = 180 * time.Second
// longPollTimeoutSec is the getUpdates long-poll timeout in seconds.
longPollTimeoutSec = 50
// pollInterval is how long to sleep between getUpdates batches.
pollInterval = 1 * time.Second
// maxUpdatesPerPoll caps the number of updates fetched in one getUpdates call.
maxUpdatesPerPoll = 100
// maxMessageLen is Telegram's per-message text limit.
maxMessageLen = 4096
)
// Config holds the settings required to reach the Telegram Bot API.
type Config struct {
// BotToken is the secret token issued by BotFather. It is stored and
// surfaced through Config() but never written to logs.
BotToken string `json:"bot_token"`
// WebhookURL is the public HTTPS endpoint Telegram delivers updates to.
// When empty the adapter uses getUpdates long polling instead.
WebhookURL string `json:"webhook_url"`
}
// Option customizes an Adapter at construction time.
type Option func(*Adapter)
// WithHTTPClient injects the *http.Client used for Bot API requests. Tests pass
// the client of an httptest.Server so the mock endpoint answers calls.
func WithHTTPClient(c *http.Client) Option {
return func(a *Adapter) { a.client = c }
}
// WithBaseURL overrides the Telegram API root (default https://api.telegram.org).
// Tests point it at an httptest.Server URL.
func WithBaseURL(base string) Option {
return func(a *Adapter) {
if base != "" {
a.baseURL = base
}
}
}
// WithLogger injects the logger used by the adapter. When nil the adapter logs
// through a discard logger so a bare adapter stays silent; callers wire their
// production logger via this option.
func WithLogger(l *slog.Logger) Option {
return func(a *Adapter) {
if l == nil {
l = logger.New(logger.Options{Out: io.Discard})
}
a.log = logger.WithSource(l, "plugins/channels/telegram")
}
}
// New builds a Telegram adapter. It performs no network I/O; call Connect to
// establish the session.
func New(cfg Config, opts ...Option) *Adapter {
a := &Adapter{
cfg: cfg,
client: &http.Client{Timeout: httpTimeout},
baseURL: defaultBaseURL,
log: logger.WithSource(logger.New(logger.Options{Out: io.Discard}), "plugins/channels/telegram"),
chats: map[string]adapter.ChannelInfo{},
}
for _, opt := range opts {
opt(a)
}
return a
}
// endpoint returns the absolute Bot API URL for the given method, bundling the
// token into the path (never into query parameters or logs).
func (a *Adapter) endpoint(method string) string {
return a.baseURL + "/bot" + a.cfg.BotToken + "/" + method
}