feat(channels/telegram): Telegram 渠道插件(Bot API 长轮询/Webhook + file_id 媒体 + 主动推送强)
This commit is contained in:
1 parent
0b036645bb
commit
f9f5a78a02
12 files changed
+1983
No files matched your search
@@ -0,0 +1,90 @@
|
||||
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
|
||||
}
|
||||
Reference in new issue
Block a user