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 }