Files
lukaszraczylo ac7cae8fa7 Initial release of go-telegram
A fully-generated, strongly-typed Go client for the Telegram Bot API.

* 176 methods + 301 types generated from Bot API v10.0
* 1408 auto-generated tests (8 scenarios per method)
* Typed unions throughout — no 'any' in the public surface
* Pluggable HTTP transport and JSON codec (default goccy/go-json)
* Built-in retry middleware honouring Telegram's retry_after
* Generic dispatcher with filters and conversation handlers
* Self-verifying codegen pipeline (regen → audit → emit → run tests)
* 14 example bots covering common patterns
2026-05-09 13:09:27 +01:00

144 lines
4.4 KiB
Go

// 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
}