Documentation
¶
Index ¶
- func IsSafeSplitPosition(pos int, codeBlocks []CodeBlock) bool
- func SplitMessageSmart(text string, limit int) []string
- type APIResponse
- type BotAPI
- type BotCommand
- type Chat
- type Client
- func (c *Client) GetFile(ctx context.Context, req GetFileRequest) (*File, error)
- func (c *Client) GetUpdates(ctx context.Context, req GetUpdatesRequest) ([]Update, error)
- func (c *Client) SendChatAction(ctx context.Context, req SendChatActionRequest) error
- func (c *Client) SendMessage(ctx context.Context, req SendMessageRequest) (*Message, error)
- func (c *Client) SetMessageReaction(ctx context.Context, req SetMessageReactionRequest) error
- func (c *Client) SetMyCommands(ctx context.Context, req SetMyCommandsRequest) error
- func (c *Client) SetWebhook(ctx context.Context, req SetWebhookRequest) error
- type CodeBlock
- type Document
- type ExtendedClient
- type File
- type FileDownloader
- type GetFileRequest
- type GetUpdatesRequest
- type HTTPFileDownloader
- type Message
- type MessageOrigin
- type PhotoSize
- type ReactionType
- type SendChatActionRequest
- type SendMessageRequest
- type SetMessageReactionRequest
- type SetMyCommandsRequest
- type SetWebhookRequest
- type Update
- type User
- type Voice
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func IsSafeSplitPosition ¶
IsSafeSplitPosition checks if a position is safe for splitting (not inside code blocks)
func SplitMessageSmart ¶
SplitMessageSmart splits a long message into chunks that are safe for Telegram's message length limit. It preserves markdown structure by avoiding splits inside code blocks and other markdown constructs.
Types ¶
type APIResponse ¶
type APIResponse struct {
Ok bool `json:"ok"`
Result json.RawMessage `json:"result,omitempty"`
Description string `json:"description,omitempty"`
ErrorCode int `json:"error_code,omitempty"`
}
APIResponse represents a response from the Telegram API.
type BotAPI ¶
type BotAPI interface {
SendMessage(ctx context.Context, req SendMessageRequest) (*Message, error)
SetMyCommands(ctx context.Context, req SetMyCommandsRequest) error
SetWebhook(ctx context.Context, req SetWebhookRequest) error
SendChatAction(ctx context.Context, req SendChatActionRequest) error
GetFile(ctx context.Context, req GetFileRequest) (*File, error)
SetMessageReaction(ctx context.Context, req SetMessageReactionRequest) error
GetUpdates(ctx context.Context, req GetUpdatesRequest) ([]Update, error)
GetToken() string
}
BotAPI defines the interface for the Telegram Bot API methods we use. This allows for easier mocking in tests.
func NewExtendedClient ¶
type BotCommand ¶
BotCommand represents a bot command.
type Chat ¶
type Chat struct {
ID int64 `json:"id"`
Type string `json:"type"`
Title string `json:"title,omitempty"`
Username string `json:"username,omitempty"`
}
Chat represents a chat.
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client is a client for the Telegram Bot API.
АРХИТЕКТУРНОЕ РЕШЕНИЕ: Два отдельных HTTP-клиента
Проблема: При использовании одного HTTP-клиента для всех запросов возникали спорадические ошибки "context deadline exceeded (Client.Timeout exceeded while awaiting headers)" для sendMessage, sendChatAction и getUpdates.
Причина: Long polling (getUpdates с Timeout=25s) "занимал" соединения из общего пула на длительное время. При высокой нагрузке или сетевых задержках другие запросы не могли получить свободное соединение и таймаутились.
Решение:
- httpClient - для коротких API-вызовов (sendMessage, sendChatAction и т.д.) с таймаутом 15 секунд и retry-логикой
- longPollingClient - для getUpdates с бесконечным таймаутом (контролируется через context), изолированный пул соединений
Дополнительно отключён HTTP/2 (ForceAttemptHTTP2=false), так как мультиплексирование запросов через одно соединение усугубляло проблему с конкуренцией за ресурсы.
func NewClient ¶
NewClient creates a new Telegram API client.
Создаёт два изолированных HTTP-клиента с разными настройками: - httpClient: таймаут 30s, retry-логика, для sendMessage/sendChatAction - longPollingClient: без таймаута (контроль через context), для getUpdates
func (*Client) GetFile ¶
GetFile returns a File object with a file_path that can be used to download the file.
func (*Client) GetUpdates ¶
GetUpdates receives incoming updates using long polling.
ВАЖНО: Использует отдельный longPollingClient с Timeout=0.
Long polling работает так: Telegram держит соединение открытым до req.Timeout секунд, ожидая новые сообщения. Если сообщения приходят раньше - возвращает их сразу. Если нет - возвращает пустой массив по истечении таймаута.
Почему отдельный клиент: - httpClient имеет таймаут 15s, что меньше типичного req.Timeout (25s) - Использование общего пула соединений создавало конкуренцию с короткими запросами - Изолированный transport гарантирует, что long polling не влияет на sendMessage
Таймаут контролируется через context: req.Timeout + 10 секунд на сетевые задержки.
Метрики: записывает время выполнения, статус long polling и количество updates.
func (*Client) SendChatAction ¶
func (c *Client) SendChatAction(ctx context.Context, req SendChatActionRequest) error
SendChatAction tells the user that something is happening on the bot's side.
func (*Client) SendMessage ¶
SendMessage sends a text message.
func (*Client) SetMessageReaction ¶
func (c *Client) SetMessageReaction(ctx context.Context, req SetMessageReactionRequest) error
SetMessageReaction sets a reaction on a message.
func (*Client) SetMyCommands ¶
func (c *Client) SetMyCommands(ctx context.Context, req SetMyCommandsRequest) error
SetMyCommands changes the list of the bot's commands.
func (*Client) SetWebhook ¶
func (c *Client) SetWebhook(ctx context.Context, req SetWebhookRequest) error
SetWebhook specifies a URL and receives incoming updates via an outgoing webhook.
type CodeBlock ¶
CodeBlock represents a code block with its start and end positions
func FindCodeBlocks ¶
FindCodeBlocks finds all code blocks in the text and returns their positions
type Document ¶
type Document struct {
FileID string `json:"file_id"`
FileUniqueID string `json:"file_unique_id"`
FileName string `json:"file_name,omitempty"`
MimeType string `json:"mime_type,omitempty"`
FileSize int `json:"file_size,omitempty"`
}
Document represents a general file (as opposed to photos, voice messages and audio files).
type ExtendedClient ¶
type ExtendedClient struct {
*Client
}
Wrapper for telegram.Client to implement BotAPI interface
func (*ExtendedClient) GetToken ¶
func (c *ExtendedClient) GetToken() string
type File ¶
type File struct {
FileID string `json:"file_id"`
FileUniqueID string `json:"file_unique_id"`
FileSize int `json:"file_size,omitempty"`
FilePath string `json:"file_path,omitempty"`
}
File represents a file ready to be downloaded.
type FileDownloader ¶
type FileDownloader interface {
DownloadFile(ctx context.Context, fileID string) ([]byte, error)
DownloadFileAsBase64(ctx context.Context, fileID string) (string, error)
}
FileDownloader defines an interface for downloading files from Telegram.
type GetFileRequest ¶
type GetFileRequest struct {
FileID string `json:"file_id"`
}
GetFileRequest represents the parameters for the getFile method.
type GetUpdatesRequest ¶
type GetUpdatesRequest struct {
Offset int `json:"offset,omitempty"`
Limit int `json:"limit,omitempty"`
Timeout int `json:"timeout,omitempty"`
AllowedUpdates []string `json:"allowed_updates,omitempty"`
}
GetUpdatesRequest represents the parameters for the getUpdates method.
type HTTPFileDownloader ¶
type HTTPFileDownloader struct {
// contains filtered or unexported fields
}
HTTPFileDownloader is a concrete implementation of FileDownloader using HTTP.
func NewHTTPFileDownloader ¶
func NewHTTPFileDownloader(api BotAPI, fileBaseURL, proxyURL string) (*HTTPFileDownloader, error)
NewHTTPFileDownloader creates a new HTTPFileDownloader.
HTTP client configured with: - 60s timeout for large file downloads - DisableKeepAlives to avoid connection pool issues - Reasonable timeouts for dial/TLS/headers - Proxy support (uses proxyURL if provided, otherwise falls back to environment)
func (*HTTPFileDownloader) DownloadFile ¶
DownloadFile downloads a file from Telegram.
func (*HTTPFileDownloader) DownloadFileAsBase64 ¶
func (d *HTTPFileDownloader) DownloadFileAsBase64(ctx context.Context, fileID string) (string, error)
DownloadFileAsBase64 downloads a file and encodes it as a Base64 string.
type Message ¶
type Message struct {
MessageID int `json:"message_id"`
MessageThreadID int `json:"message_thread_id,omitempty"`
From *User `json:"from,omitempty"`
Chat *Chat `json:"chat"`
Date int `json:"date"`
Text string `json:"text,omitempty"`
Caption string `json:"caption,omitempty"`
Photo []PhotoSize `json:"photo,omitempty"`
Document *Document `json:"document,omitempty"`
Voice *Voice `json:"voice,omitempty"`
ForwardOrigin *MessageOrigin `json:"forward_origin,omitempty"`
}
Message represents a message.
func (*Message) BuildContent ¶
func (m *Message) BuildContent(translator *i18n.Translator, lang string) string
BuildContent constructs the full text content for a message, including prefixes for user, time, and forwarding.
func (*Message) BuildPrefix ¶ added in v0.2.0
func (m *Message) BuildPrefix(translator *i18n.Translator, lang string) string
BuildPrefix constructs the prefix for a message (user info or forwarding info).
type MessageOrigin ¶
type MessageOrigin struct {
Type string `json:"type"`
Date int `json:"date"`
SenderUser *User `json:"sender_user,omitempty"`
SenderUserName string `json:"sender_user_name,omitempty"`
SenderChat *Chat `json:"sender_chat,omitempty"`
AuthorSignature string `json:"author_signature,omitempty"`
}
MessageOrigin represents the origin of a message.
func (*MessageOrigin) Format ¶
func (mo *MessageOrigin) Format(forwardedBy *User, translator *i18n.Translator, lang string) string
Format formats the forward origin information for display.
type PhotoSize ¶
type PhotoSize struct {
FileID string `json:"file_id"`
FileUniqueID string `json:"file_unique_id"`
Width int `json:"width"`
Height int `json:"height"`
FileSize int `json:"file_size,omitempty"`
}
PhotoSize represents one size of a photo or a file / sticker thumbnail.
type ReactionType ¶
ReactionType represents a reaction type.
type SendChatActionRequest ¶
type SendChatActionRequest struct {
ChatID int64 `json:"chat_id"`
MessageThreadID *int `json:"message_thread_id,omitempty"`
Action string `json:"action"`
}
SendChatActionRequest represents the parameters for the sendChatAction method. См. комментарий к SendMessageRequest о причине использования *int для MessageThreadID.
type SendMessageRequest ¶
type SendMessageRequest struct {
ChatID int64 `json:"chat_id"`
MessageThreadID *int `json:"message_thread_id,omitempty"`
Text string `json:"text"`
ParseMode string `json:"parse_mode,omitempty"`
ReplyToMessageID int `json:"reply_to_message_id,omitempty"`
}
SendMessageRequest represents the parameters for the sendMessage method.
ВАЖНО: MessageThreadID использует *int вместо int, чтобы omitempty корректно работал для нулевого значения. Telegram API интерпретирует message_thread_id: 0 как попытку отправить в топик с ID=0, что вызывает ошибку "Bad Request: invalid topic identifier specified" в обычных чатах (не форумах). При использовании указателя nil не сериализуется в JSON вообще.
type SetMessageReactionRequest ¶
type SetMessageReactionRequest struct {
ChatID int64 `json:"chat_id"`
MessageID int `json:"message_id"`
Reaction []ReactionType `json:"reaction,omitempty"`
IsBig bool `json:"is_big,omitempty"`
}
SetMessageReactionRequest represents the parameters for the setMessageReaction method.
type SetMyCommandsRequest ¶
type SetMyCommandsRequest struct {
Commands []BotCommand `json:"commands"`
}
SetMyCommandsRequest represents the parameters for the setMyCommands method.
type SetWebhookRequest ¶
type SetWebhookRequest struct {
URL string `json:"url"`
SecretToken string `json:"secret_token,omitempty"`
}
SetWebhookRequest represents the parameters for the setWebhook method.