// Package api contains the Telegram Bot API types and method wrappers. // Most of the package is generated by cmd/genapi from internal/spec/api.json; // this file holds the runtime types that are intentionally hand-coded. // // InputFile carries either a local upload (Reader+Filename) or a reference // to a previously-uploaded file (file_id) / URL Telegram can fetch. It is // not a pure JSON type, so the codegen skips it (see runtimeTypes in // cmd/genapi/emitter.go). // // ResponseParameters mirrors client.ResponseParameters so callers importing // only `api` can access retry_after and migrate_to_chat_id without pulling // in the client package. package api import ( "bytes" "fmt" "github.com/goccy/go-json" "io" "strconv" ) // InputFile carries either a file path (for upload) or a Telegram file_id // / URL string (for reuse). When PathOrID names a local file, the request // is sent as multipart/form-data; otherwise the value is sent inline. type InputFile struct { // PathOrID is one of: an absolute or relative filesystem path, a // previously-uploaded Telegram file_id, or an HTTPS URL Telegram // can fetch. PathOrID string // Reader, when non-nil, is used as the file content (Filename names it). Reader io.Reader // Filename is the upload filename used when Reader is set. Filename string } // IsLocalUpload reports whether this InputFile triggers a multipart upload. func (f *InputFile) IsLocalUpload() bool { if f == nil { return false } return f.Reader != nil } // ResponseParameters is the optional metadata Telegram includes on certain // failures. The most common is RetryAfter (seconds) on 429 responses. // // https://core.telegram.org/bots/api#responseparameters type ResponseParameters struct { MigrateToChatID int64 `json:"migrate_to_chat_id,omitempty"` RetryAfter int `json:"retry_after,omitempty"` } // ChatID identifies a chat by either numeric id or "@username". The Telegram // Bot API spells the same field as either an integer or a string; ChatID // preserves both forms with explicit constructors and a custom MarshalJSON // so callers never see `any` at the source level. type ChatID struct { int64Set bool intID int64 username string } // ChatIDFromInt builds a ChatID for a numeric chat identifier (e.g. -1001234567890). func ChatIDFromInt(id int64) ChatID { return ChatID{int64Set: true, intID: id} } // ChatIDFromUsername builds a ChatID for a public chat (e.g. "@channel"). // The leading "@" is required by Telegram for usernames. func ChatIDFromUsername(name string) ChatID { return ChatID{username: name} } // IsZero reports whether c carries no value. func (c ChatID) IsZero() bool { return !c.int64Set && c.username == "" } // String returns the wire form (decimal integer or "@name") for use in // multipart bodies. func (c ChatID) String() string { if c.int64Set { return strconv.FormatInt(c.intID, 10) } return c.username } // MarshalJSON emits either a JSON number (integer form) or a JSON string // (@username form). Empty values marshal as "null". func (c ChatID) MarshalJSON() ([]byte, error) { if c.int64Set { return []byte(strconv.FormatInt(c.intID, 10)), nil } if c.username != "" { return json.Marshal(c.username) } return []byte("null"), nil } // UnmarshalJSON accepts either a JSON number or a JSON string. func (c *ChatID) UnmarshalJSON(data []byte) error { data = bytes.TrimSpace(data) if len(data) == 0 || bytes.Equal(data, []byte("null")) { *c = ChatID{} return nil } if data[0] == '"' { var s string if err := json.Unmarshal(data, &s); err != nil { return err } *c = ChatIDFromUsername(s) return nil } var n int64 if err := json.Unmarshal(data, &n); err != nil { return fmt.Errorf("ChatID: %w", err) } *c = ChatIDFromInt(n) return nil } // MessageOrBool wraps the "Message or True" return shape Telegram uses on // edit methods (editMessageText, editMessageCaption, etc.). When the bot // edits a regular chat message, Message is non-nil; when it edits an // inline message, OK is true. type MessageOrBool struct { Message *Message OK bool } // UnmarshalJSON decodes either {...} into Message or `true`/`false` into OK. func (m *MessageOrBool) UnmarshalJSON(data []byte) error { data = bytes.TrimSpace(data) if len(data) == 0 { return nil } if data[0] == '{' { m.Message = new(Message) return json.Unmarshal(data, m.Message) } var b bool if err := json.Unmarshal(data, &b); err != nil { return fmt.Errorf("MessageOrBool: %w", err) } m.OK = b return nil }