mirror of
https://github.com/lukaszraczylo/go-telegram.git
synced 2026-06-05 22:43:59 +00:00
75c7ce3119
Two changes that together cut allocs/call from 15 to 13 (client-internal bench) and per-call CPU from 600ns to 455ns (-24%) on the no-HTTP path: 1. Codec gets an optional BodyEncoder extension (MarshalTo io.Writer). When present, encodeJSONBody stream-encodes the request directly into a pooled *bytes.Buffer instead of allocating a [2-step] Marshal+Reader pair. DefaultCodec implements it via goccy/go-json.NewEncoder. 2. *Bot caches the parsed base URL on construction. buildRequest skips net/http.NewRequestWithContext for the common case and constructs *http.Request manually — clones the URL by value, sets the method path, and populates ContentLength + GetBody from the body's concrete type so RetryDoer's body-replay across attempts still works. Cross-library bench (sendMessage round-trip vs httptest.Server): -2 allocs/call (104 -> 102), bytes -1.2%, time within noise (real HTTP plumbing dominates). The CPU win is visible on synthetic stub-doer benches and translates to lower GC pressure on sustained-throughput workloads. Slow-path fallback preserved for codecs that don't implement BodyEncoder and for *Bot instances where url.Parse on the configured base failed — they take the original NewRequestWithContext path.
68 lines
2.1 KiB
Go
68 lines
2.1 KiB
Go
package client
|
|
|
|
import (
|
|
"net/url"
|
|
)
|
|
|
|
const defaultBaseURL = "https://api.telegram.org"
|
|
|
|
// Bot is the Telegram Bot API client. Construct via New. All API methods
|
|
// (declared in package api) hang off *Bot via thin wrappers around call.
|
|
type Bot struct {
|
|
token string
|
|
base string
|
|
http HTTPDoer
|
|
codec Codec
|
|
logger Logger
|
|
|
|
// baseURL is the parsed form of base, lazily populated on first Call.
|
|
// Caching it avoids running url.Parse on every API request.
|
|
baseURL *url.URL
|
|
// pathPrefix is "/bot<token>/" built once so per-call URL assembly
|
|
// is a single string concatenation with the method name.
|
|
pathPrefix string
|
|
}
|
|
|
|
// Token returns the bot token. Exposed for advanced use cases (custom
|
|
// transports, manual URL building); ordinary code does not need it.
|
|
func (b *Bot) Token() string { return b.token }
|
|
|
|
// BaseURL returns the configured Telegram API base URL.
|
|
func (b *Bot) BaseURL() string { return b.base }
|
|
|
|
// HTTP returns the underlying HTTPDoer. Exposed for adapters that need
|
|
// to share connection pools or for diagnostic checks.
|
|
func (b *Bot) HTTP() HTTPDoer { return b.http }
|
|
|
|
// Codec returns the configured Codec.
|
|
func (b *Bot) Codec() Codec { return b.codec }
|
|
|
|
// Logger returns the configured Logger.
|
|
func (b *Bot) Logger() Logger { return b.logger }
|
|
|
|
// New constructs a Bot with the given token and optional configuration.
|
|
// The default HTTP client is tuned for long-poll workloads (see
|
|
// NewDefaultHTTPDoer); the default codec wraps encoding/json; the default
|
|
// logger discards records.
|
|
func New(token string, opts ...Option) *Bot {
|
|
b := &Bot{
|
|
token: token,
|
|
base: defaultBaseURL,
|
|
http: NewDefaultHTTPDoer(),
|
|
codec: DefaultCodec{},
|
|
logger: NoopLogger{},
|
|
}
|
|
for _, o := range opts {
|
|
o(b)
|
|
}
|
|
// Pre-compute URL pieces. Errors here are unlikely (defaultBaseURL is
|
|
// well-formed; user-supplied bases via WithBaseURL are validated by
|
|
// url.Parse below) but if parsing fails we leave baseURL nil and fall
|
|
// back to the string-concat path on the next Call.
|
|
if u, err := url.Parse(b.base); err == nil {
|
|
b.baseURL = u
|
|
}
|
|
b.pathPrefix = "/bot" + b.token + "/"
|
|
return b
|
|
}
|