mirror of
https://github.com/lukaszraczylo/go-telegram.git
synced 2026-06-05 22:43:59 +00:00
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
This commit is contained in:
Vendored
+175
@@ -0,0 +1,175 @@
|
||||
{
|
||||
"version": "7.10",
|
||||
"types": [
|
||||
{
|
||||
"name": "User",
|
||||
"doc": "This object represents a Telegram user or bot.",
|
||||
"fields": [
|
||||
{
|
||||
"name": "ID",
|
||||
"json_name": "id",
|
||||
"type": {
|
||||
"kind": "primitive",
|
||||
"name": "int64"
|
||||
},
|
||||
"required": true,
|
||||
"doc": "Unique identifier."
|
||||
},
|
||||
{
|
||||
"name": "IsBot",
|
||||
"json_name": "is_bot",
|
||||
"type": {
|
||||
"kind": "primitive",
|
||||
"name": "bool"
|
||||
},
|
||||
"required": true,
|
||||
"doc": "True, if this user is a bot."
|
||||
},
|
||||
{
|
||||
"name": "FirstName",
|
||||
"json_name": "first_name",
|
||||
"type": {
|
||||
"kind": "primitive",
|
||||
"name": "string"
|
||||
},
|
||||
"required": true,
|
||||
"doc": "User's or bot's first name."
|
||||
},
|
||||
{
|
||||
"name": "LastName",
|
||||
"json_name": "last_name",
|
||||
"type": {
|
||||
"kind": "primitive",
|
||||
"name": "string"
|
||||
},
|
||||
"doc": "Optional. User's or bot's last name."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "ChatMember",
|
||||
"doc": "This object contains information about one member of a chat. Currently, the following 6 types of chat members are supported:",
|
||||
"one_of": [
|
||||
"ChatMemberOwner",
|
||||
"ChatMemberAdministrator"
|
||||
]
|
||||
}
|
||||
],
|
||||
"methods": [
|
||||
{
|
||||
"name": "getMe",
|
||||
"doc": "A simple method for testing your bot's authentication token. Requires no parameters. Returns basic information about the bot in form of a User object.",
|
||||
"returns": {
|
||||
"kind": "named",
|
||||
"name": "User"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "sendMessage",
|
||||
"doc": "Use this method to send text messages. On success, the sent Message is returned.",
|
||||
"params": [
|
||||
{
|
||||
"name": "ChatID",
|
||||
"json_name": "chat_id",
|
||||
"type": {
|
||||
"kind": "oneOf",
|
||||
"variants": [
|
||||
"int64",
|
||||
"string"
|
||||
]
|
||||
},
|
||||
"required": true,
|
||||
"doc": "Unique identifier for the target chat."
|
||||
},
|
||||
{
|
||||
"name": "Text",
|
||||
"json_name": "text",
|
||||
"type": {
|
||||
"kind": "primitive",
|
||||
"name": "string"
|
||||
},
|
||||
"required": true,
|
||||
"doc": "Text of the message to be sent."
|
||||
},
|
||||
{
|
||||
"name": "ParseMode",
|
||||
"json_name": "parse_mode",
|
||||
"type": {
|
||||
"kind": "primitive",
|
||||
"name": "string"
|
||||
},
|
||||
"doc": "Mode for parsing entities in the message text."
|
||||
}
|
||||
],
|
||||
"returns": {
|
||||
"kind": "named",
|
||||
"name": "Message"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "sendDocument",
|
||||
"doc": "Use this method to send general files. On success, the sent Message is returned.",
|
||||
"params": [
|
||||
{
|
||||
"name": "ChatID",
|
||||
"json_name": "chat_id",
|
||||
"type": {
|
||||
"kind": "primitive",
|
||||
"name": "int64"
|
||||
},
|
||||
"required": true,
|
||||
"doc": "Unique identifier for the target chat."
|
||||
},
|
||||
{
|
||||
"name": "Document",
|
||||
"json_name": "document",
|
||||
"type": {
|
||||
"kind": "oneOf",
|
||||
"variants": [
|
||||
"InputFile",
|
||||
"string"
|
||||
]
|
||||
},
|
||||
"required": true,
|
||||
"doc": "File to send."
|
||||
},
|
||||
{
|
||||
"name": "Caption",
|
||||
"json_name": "caption",
|
||||
"type": {
|
||||
"kind": "primitive",
|
||||
"name": "string"
|
||||
},
|
||||
"doc": "Document caption."
|
||||
}
|
||||
],
|
||||
"returns": {
|
||||
"kind": "named",
|
||||
"name": "Message"
|
||||
},
|
||||
"has_files": true
|
||||
},
|
||||
{
|
||||
"name": "getUpdates",
|
||||
"doc": "Use this method to receive incoming updates using long polling. Returns an Array of Update objects.",
|
||||
"params": [
|
||||
{
|
||||
"name": "Limit",
|
||||
"json_name": "limit",
|
||||
"type": {
|
||||
"kind": "primitive",
|
||||
"name": "int64"
|
||||
},
|
||||
"doc": "Limits the number of updates to be retrieved."
|
||||
}
|
||||
],
|
||||
"returns": {
|
||||
"kind": "array",
|
||||
"elem_type": {
|
||||
"kind": "named",
|
||||
"name": "Update"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
Vendored
+60
@@ -0,0 +1,60 @@
|
||||
// Code generated by cmd/genapi. DO NOT EDIT.
|
||||
|
||||
//go:build !ignore_autogenerated
|
||||
|
||||
package api
|
||||
|
||||
// ParseMode controls how Telegram interprets formatting in message text.
|
||||
type ParseMode string
|
||||
|
||||
const (
|
||||
ParseModeMarkdown ParseMode = "Markdown" // legacy
|
||||
ParseModeMarkdownV2 ParseMode = "MarkdownV2"
|
||||
ParseModeHTML ParseMode = "HTML"
|
||||
)
|
||||
|
||||
// ChatType is the type of a Telegram chat.
|
||||
type ChatType string
|
||||
|
||||
const (
|
||||
ChatTypePrivate ChatType = "private"
|
||||
ChatTypeGroup ChatType = "group"
|
||||
ChatTypeSupergroup ChatType = "supergroup"
|
||||
ChatTypeChannel ChatType = "channel"
|
||||
)
|
||||
|
||||
// UpdateType identifies an Update payload variant. Used by allowed_updates
|
||||
// in getUpdates / setWebhook.
|
||||
type UpdateType string
|
||||
|
||||
const (
|
||||
UpdateMessage UpdateType = "message"
|
||||
UpdateEditedMessage UpdateType = "edited_message"
|
||||
UpdateChannelPost UpdateType = "channel_post"
|
||||
UpdateEditedChannelPost UpdateType = "edited_channel_post"
|
||||
UpdateCallbackQuery UpdateType = "callback_query"
|
||||
UpdateInlineQuery UpdateType = "inline_query"
|
||||
)
|
||||
|
||||
// MessageEntityType is the kind of an entity (mention, hashtag, command, ...).
|
||||
type MessageEntityType string
|
||||
|
||||
const (
|
||||
EntityMention MessageEntityType = "mention"
|
||||
EntityHashtag MessageEntityType = "hashtag"
|
||||
EntityCashtag MessageEntityType = "cashtag"
|
||||
EntityBotCommand MessageEntityType = "bot_command"
|
||||
EntityURL MessageEntityType = "url"
|
||||
EntityEmail MessageEntityType = "email"
|
||||
EntityPhoneNumber MessageEntityType = "phone_number"
|
||||
EntityBold MessageEntityType = "bold"
|
||||
EntityItalic MessageEntityType = "italic"
|
||||
EntityUnderline MessageEntityType = "underline"
|
||||
EntityStrike MessageEntityType = "strikethrough"
|
||||
EntitySpoiler MessageEntityType = "spoiler"
|
||||
EntityCode MessageEntityType = "code"
|
||||
EntityPre MessageEntityType = "pre"
|
||||
EntityTextLink MessageEntityType = "text_link"
|
||||
EntityTextMention MessageEntityType = "text_mention"
|
||||
EntityCustomEmoji MessageEntityType = "custom_emoji"
|
||||
)
|
||||
Vendored
+113
@@ -0,0 +1,113 @@
|
||||
// Code generated by cmd/genapi. DO NOT EDIT.
|
||||
|
||||
//go:build !ignore_autogenerated
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/goccy/go-json"
|
||||
"strconv"
|
||||
|
||||
"github.com/lukaszraczylo/go-telegram/client"
|
||||
)
|
||||
|
||||
var _ = strconv.Itoa // keep import for multipart helpers
|
||||
var _ = json.Marshal // keep import for complex multipart fields
|
||||
|
||||
// GetMeParams is the parameter set for GetMe.
|
||||
//
|
||||
// A simple method for testing your bot's authentication token. Requires no parameters. Returns basic information about the bot in form of a User object.
|
||||
type GetMeParams struct {
|
||||
}
|
||||
|
||||
// GetMe calls the getMe Telegram Bot API method.
|
||||
//
|
||||
// A simple method for testing your bot's authentication token. Requires no parameters. Returns basic information about the bot in form of a User object.
|
||||
func GetMe(ctx context.Context, b *client.Bot, p *GetMeParams) (*User, error) {
|
||||
return client.Call[*GetMeParams, *User](ctx, b, "getMe", p)
|
||||
}
|
||||
|
||||
// SendMessageParams is the parameter set for SendMessage.
|
||||
//
|
||||
// Use this method to send text messages. On success, the sent Message is returned.
|
||||
type SendMessageParams struct {
|
||||
// Unique identifier for the target chat.
|
||||
ChatID ChatID `json:"chat_id"`
|
||||
// Text of the message to be sent.
|
||||
Text string `json:"text"`
|
||||
// Mode for parsing entities in the message text.
|
||||
ParseMode string `json:"parse_mode,omitempty"`
|
||||
}
|
||||
|
||||
// SendMessage calls the sendMessage Telegram Bot API method.
|
||||
//
|
||||
// Use this method to send text messages. On success, the sent Message is returned.
|
||||
func SendMessage(ctx context.Context, b *client.Bot, p *SendMessageParams) (*Message, error) {
|
||||
return client.Call[*SendMessageParams, *Message](ctx, b, "sendMessage", p)
|
||||
}
|
||||
|
||||
// SendDocumentParams is the parameter set for SendDocument.
|
||||
//
|
||||
// Use this method to send general files. On success, the sent Message is returned.
|
||||
type SendDocumentParams struct {
|
||||
// Unique identifier for the target chat.
|
||||
ChatID int64 `json:"chat_id"`
|
||||
// File to send.
|
||||
Document *InputFile `json:"document"`
|
||||
// Document caption.
|
||||
Caption string `json:"caption,omitempty"`
|
||||
}
|
||||
|
||||
// HasFile reports whether a multipart upload is required.
|
||||
func (p *SendDocumentParams) HasFile() bool {
|
||||
if p.Document != nil && p.Document.IsLocalUpload() {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// MultipartFields returns the non-file fields used in the multipart body.
|
||||
func (p *SendDocumentParams) MultipartFields() map[string]string {
|
||||
out := map[string]string{}
|
||||
out["chat_id"] = strconv.FormatInt(p.ChatID, 10)
|
||||
if p.Caption != "" {
|
||||
out["caption"] = p.Caption
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// MultipartFiles returns the file parts.
|
||||
func (p *SendDocumentParams) MultipartFiles() []client.MultipartFile {
|
||||
var files []client.MultipartFile
|
||||
if p.Document != nil && p.Document.IsLocalUpload() {
|
||||
name := p.Document.Filename
|
||||
if name == "" {
|
||||
name = "document"
|
||||
}
|
||||
files = append(files, client.MultipartFile{FieldName: "document", Filename: name, Reader: p.Document.Reader})
|
||||
}
|
||||
return files
|
||||
}
|
||||
|
||||
// SendDocument calls the sendDocument Telegram Bot API method.
|
||||
//
|
||||
// Use this method to send general files. On success, the sent Message is returned.
|
||||
func SendDocument(ctx context.Context, b *client.Bot, p *SendDocumentParams) (*Message, error) {
|
||||
return client.Call[*SendDocumentParams, *Message](ctx, b, "sendDocument", p)
|
||||
}
|
||||
|
||||
// GetUpdatesParams is the parameter set for GetUpdates.
|
||||
//
|
||||
// Use this method to receive incoming updates using long polling. Returns an Array of Update objects.
|
||||
type GetUpdatesParams struct {
|
||||
// Limits the number of updates to be retrieved.
|
||||
Limit *int64 `json:"limit,omitempty"`
|
||||
}
|
||||
|
||||
// GetUpdates calls the getUpdates Telegram Bot API method.
|
||||
//
|
||||
// Use this method to receive incoming updates using long polling. Returns an Array of Update objects.
|
||||
func GetUpdates(ctx context.Context, b *client.Bot, p *GetUpdatesParams) ([]Update, error) {
|
||||
return client.Call[*GetUpdatesParams, []Update](ctx, b, "getUpdates", p)
|
||||
}
|
||||
Vendored
+565
@@ -0,0 +1,565 @@
|
||||
// Code generated by cmd/genapi. DO NOT EDIT.
|
||||
|
||||
//go:build !ignore_autogenerated
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/lukaszraczylo/go-telegram/client"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// genTestMockDoer is a testify-mock HTTPDoer used by generated tests only.
|
||||
type genTestMockDoer struct{ mock.Mock }
|
||||
|
||||
func (m *genTestMockDoer) Do(r *http.Request) (*http.Response, error) {
|
||||
args := m.Called(r)
|
||||
if v := args.Get(0); v != nil {
|
||||
return v.(*http.Response), args.Error(1)
|
||||
}
|
||||
return nil, args.Error(1)
|
||||
}
|
||||
|
||||
func genTestResp(status int, body string) *http.Response {
|
||||
return &http.Response{
|
||||
StatusCode: status,
|
||||
Body: io.NopCloser(bytes.NewBufferString(body)),
|
||||
Header: http.Header{"Content-Type": []string{"application/json"}},
|
||||
}
|
||||
}
|
||||
|
||||
func Test_GetMe_Success(t *testing.T) {
|
||||
m := &genTestMockDoer{}
|
||||
m.On("Do", mock.MatchedBy(func(r *http.Request) bool {
|
||||
return strings.HasSuffix(r.URL.Path, "/getMe")
|
||||
})).Return(genTestResp(200, `{"ok":true,"result":{}}`), nil)
|
||||
|
||||
bot := client.New("test:token", client.WithHTTPClient(m))
|
||||
_, err := GetMe(context.Background(), bot, &GetMeParams{})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func Test_GetMe_APIError(t *testing.T) {
|
||||
m := &genTestMockDoer{}
|
||||
m.On("Do", mock.Anything).Return(
|
||||
genTestResp(200, `{"ok":false,"error_code":429,"description":"Too Many Requests","parameters":{"retry_after":1}}`), nil)
|
||||
|
||||
bot := client.New("test:token", client.WithHTTPClient(m))
|
||||
_, err := GetMe(context.Background(), bot, &GetMeParams{})
|
||||
require.Error(t, err)
|
||||
var ae *client.APIError
|
||||
require.ErrorAs(t, err, &ae)
|
||||
require.Equal(t, 429, ae.Code)
|
||||
require.True(t, ae.IsRetryable())
|
||||
}
|
||||
|
||||
func Test_GetMe_NetworkError(t *testing.T) {
|
||||
m := &genTestMockDoer{}
|
||||
m.On("Do", mock.Anything).Return(nil, errors.New("dial tcp: timeout"))
|
||||
|
||||
bot := client.New("test:token", client.WithHTTPClient(m))
|
||||
_, err := GetMe(context.Background(), bot, &GetMeParams{})
|
||||
require.Error(t, err)
|
||||
var ne *client.NetworkError
|
||||
require.ErrorAs(t, err, &ne)
|
||||
}
|
||||
|
||||
func Test_GetMe_ParseError(t *testing.T) {
|
||||
m := &genTestMockDoer{}
|
||||
m.On("Do", mock.Anything).Return(genTestResp(200, `not json`), nil)
|
||||
|
||||
bot := client.New("test:token", client.WithHTTPClient(m))
|
||||
_, err := GetMe(context.Background(), bot, &GetMeParams{})
|
||||
require.Error(t, err)
|
||||
var pe *client.ParseError
|
||||
require.ErrorAs(t, err, &pe)
|
||||
}
|
||||
|
||||
func Test_GetMe_ContextCanceled(t *testing.T) {
|
||||
m := &genTestMockDoer{}
|
||||
m.On("Do", mock.Anything).Return(nil, context.Canceled).Maybe()
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel()
|
||||
|
||||
bot := client.New("test:token", client.WithHTTPClient(m))
|
||||
_, err := GetMe(ctx, bot, &GetMeParams{})
|
||||
require.Error(t, err)
|
||||
require.ErrorIs(t, err, context.Canceled)
|
||||
}
|
||||
|
||||
// Test_GetMe_MissingRequiredFields exercises Telegram's server-side
|
||||
// validation: when a required field is omitted, Telegram returns 400 with
|
||||
// a description like "Bad Request: <field> is empty". The library must
|
||||
// surface this as *APIError with the ErrBadRequest sentinel.
|
||||
func Test_GetMe_MissingRequiredFields(t *testing.T) {
|
||||
m := &genTestMockDoer{}
|
||||
m.On("Do", mock.Anything).Return(
|
||||
genTestResp(200, `{"ok":false,"error_code":400,"description":"Bad Request: chat_id is empty"}`), nil)
|
||||
|
||||
bot := client.New("test:token", client.WithHTTPClient(m))
|
||||
// Send a Params with all required fields zeroed — simulates a caller
|
||||
// that forgot to populate them. The bot library marshals as-is and
|
||||
// surfaces Telegram's 400 reply.
|
||||
_, err := GetMe(context.Background(), bot, &GetMeParams{})
|
||||
require.Error(t, err)
|
||||
var ae *client.APIError
|
||||
require.ErrorAs(t, err, &ae)
|
||||
require.Equal(t, 400, ae.Code)
|
||||
require.True(t, errors.Is(err, client.ErrBadRequest))
|
||||
require.False(t, ae.IsRetryable())
|
||||
}
|
||||
|
||||
// Test_GetMe_Forbidden exercises the 403 path (bot blocked by user,
|
||||
// removed from chat, etc.). The library must surface the ErrForbidden
|
||||
// sentinel.
|
||||
func Test_GetMe_Forbidden(t *testing.T) {
|
||||
m := &genTestMockDoer{}
|
||||
m.On("Do", mock.Anything).Return(
|
||||
genTestResp(200, `{"ok":false,"error_code":403,"description":"Forbidden: bot was blocked by the user"}`), nil)
|
||||
|
||||
bot := client.New("test:token", client.WithHTTPClient(m))
|
||||
_, err := GetMe(context.Background(), bot, &GetMeParams{})
|
||||
require.Error(t, err)
|
||||
var ae *client.APIError
|
||||
require.ErrorAs(t, err, &ae)
|
||||
require.Equal(t, 403, ae.Code)
|
||||
require.True(t, errors.Is(err, client.ErrForbidden))
|
||||
require.False(t, ae.IsRetryable())
|
||||
}
|
||||
|
||||
// Test_GetMe_ServerError exercises the 5xx path. The library must
|
||||
// classify these as retryable so RetryDoer / user retry logic kicks in.
|
||||
func Test_GetMe_ServerError(t *testing.T) {
|
||||
m := &genTestMockDoer{}
|
||||
m.On("Do", mock.Anything).Return(
|
||||
genTestResp(200, `{"ok":false,"error_code":500,"description":"Internal server error"}`), nil)
|
||||
|
||||
bot := client.New("test:token", client.WithHTTPClient(m))
|
||||
_, err := GetMe(context.Background(), bot, &GetMeParams{})
|
||||
require.Error(t, err)
|
||||
var ae *client.APIError
|
||||
require.ErrorAs(t, err, &ae)
|
||||
require.Equal(t, 500, ae.Code)
|
||||
require.True(t, ae.IsRetryable(), "5xx must be retryable")
|
||||
}
|
||||
|
||||
func Test_SendMessage_Success(t *testing.T) {
|
||||
m := &genTestMockDoer{}
|
||||
m.On("Do", mock.MatchedBy(func(r *http.Request) bool {
|
||||
return strings.HasSuffix(r.URL.Path, "/sendMessage")
|
||||
})).Return(genTestResp(200, `{"ok":true,"result":{}}`), nil)
|
||||
|
||||
bot := client.New("test:token", client.WithHTTPClient(m))
|
||||
params := &SendMessageParams{
|
||||
ChatID: ChatIDFromInt(123),
|
||||
Text: "test_value",
|
||||
}
|
||||
_, err := SendMessage(context.Background(), bot, params)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func Test_SendMessage_APIError(t *testing.T) {
|
||||
m := &genTestMockDoer{}
|
||||
m.On("Do", mock.Anything).Return(
|
||||
genTestResp(200, `{"ok":false,"error_code":429,"description":"Too Many Requests","parameters":{"retry_after":1}}`), nil)
|
||||
|
||||
bot := client.New("test:token", client.WithHTTPClient(m))
|
||||
params := &SendMessageParams{
|
||||
ChatID: ChatIDFromInt(123),
|
||||
Text: "test_value",
|
||||
}
|
||||
_, err := SendMessage(context.Background(), bot, params)
|
||||
require.Error(t, err)
|
||||
var ae *client.APIError
|
||||
require.ErrorAs(t, err, &ae)
|
||||
require.Equal(t, 429, ae.Code)
|
||||
require.True(t, ae.IsRetryable())
|
||||
}
|
||||
|
||||
func Test_SendMessage_NetworkError(t *testing.T) {
|
||||
m := &genTestMockDoer{}
|
||||
m.On("Do", mock.Anything).Return(nil, errors.New("dial tcp: timeout"))
|
||||
|
||||
bot := client.New("test:token", client.WithHTTPClient(m))
|
||||
params := &SendMessageParams{
|
||||
ChatID: ChatIDFromInt(123),
|
||||
Text: "test_value",
|
||||
}
|
||||
_, err := SendMessage(context.Background(), bot, params)
|
||||
require.Error(t, err)
|
||||
var ne *client.NetworkError
|
||||
require.ErrorAs(t, err, &ne)
|
||||
}
|
||||
|
||||
func Test_SendMessage_ParseError(t *testing.T) {
|
||||
m := &genTestMockDoer{}
|
||||
m.On("Do", mock.Anything).Return(genTestResp(200, `not json`), nil)
|
||||
|
||||
bot := client.New("test:token", client.WithHTTPClient(m))
|
||||
params := &SendMessageParams{
|
||||
ChatID: ChatIDFromInt(123),
|
||||
Text: "test_value",
|
||||
}
|
||||
_, err := SendMessage(context.Background(), bot, params)
|
||||
require.Error(t, err)
|
||||
var pe *client.ParseError
|
||||
require.ErrorAs(t, err, &pe)
|
||||
}
|
||||
|
||||
func Test_SendMessage_ContextCanceled(t *testing.T) {
|
||||
m := &genTestMockDoer{}
|
||||
m.On("Do", mock.Anything).Return(nil, context.Canceled).Maybe()
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel()
|
||||
|
||||
bot := client.New("test:token", client.WithHTTPClient(m))
|
||||
params := &SendMessageParams{
|
||||
ChatID: ChatIDFromInt(123),
|
||||
Text: "test_value",
|
||||
}
|
||||
_, err := SendMessage(ctx, bot, params)
|
||||
require.Error(t, err)
|
||||
require.ErrorIs(t, err, context.Canceled)
|
||||
}
|
||||
|
||||
// Test_SendMessage_MissingRequiredFields exercises Telegram's server-side
|
||||
// validation: when a required field is omitted, Telegram returns 400 with
|
||||
// a description like "Bad Request: <field> is empty". The library must
|
||||
// surface this as *APIError with the ErrBadRequest sentinel.
|
||||
func Test_SendMessage_MissingRequiredFields(t *testing.T) {
|
||||
m := &genTestMockDoer{}
|
||||
m.On("Do", mock.Anything).Return(
|
||||
genTestResp(200, `{"ok":false,"error_code":400,"description":"Bad Request: chat_id is empty"}`), nil)
|
||||
|
||||
bot := client.New("test:token", client.WithHTTPClient(m))
|
||||
// Send a Params with all required fields zeroed — simulates a caller
|
||||
// that forgot to populate them. The bot library marshals as-is and
|
||||
// surfaces Telegram's 400 reply.
|
||||
_, err := SendMessage(context.Background(), bot, &SendMessageParams{})
|
||||
require.Error(t, err)
|
||||
var ae *client.APIError
|
||||
require.ErrorAs(t, err, &ae)
|
||||
require.Equal(t, 400, ae.Code)
|
||||
require.True(t, errors.Is(err, client.ErrBadRequest))
|
||||
require.False(t, ae.IsRetryable())
|
||||
}
|
||||
|
||||
// Test_SendMessage_Forbidden exercises the 403 path (bot blocked by user,
|
||||
// removed from chat, etc.). The library must surface the ErrForbidden
|
||||
// sentinel.
|
||||
func Test_SendMessage_Forbidden(t *testing.T) {
|
||||
m := &genTestMockDoer{}
|
||||
m.On("Do", mock.Anything).Return(
|
||||
genTestResp(200, `{"ok":false,"error_code":403,"description":"Forbidden: bot was blocked by the user"}`), nil)
|
||||
|
||||
bot := client.New("test:token", client.WithHTTPClient(m))
|
||||
params := &SendMessageParams{
|
||||
ChatID: ChatIDFromInt(123),
|
||||
Text: "test_value",
|
||||
}
|
||||
_, err := SendMessage(context.Background(), bot, params)
|
||||
require.Error(t, err)
|
||||
var ae *client.APIError
|
||||
require.ErrorAs(t, err, &ae)
|
||||
require.Equal(t, 403, ae.Code)
|
||||
require.True(t, errors.Is(err, client.ErrForbidden))
|
||||
require.False(t, ae.IsRetryable())
|
||||
}
|
||||
|
||||
// Test_SendMessage_ServerError exercises the 5xx path. The library must
|
||||
// classify these as retryable so RetryDoer / user retry logic kicks in.
|
||||
func Test_SendMessage_ServerError(t *testing.T) {
|
||||
m := &genTestMockDoer{}
|
||||
m.On("Do", mock.Anything).Return(
|
||||
genTestResp(200, `{"ok":false,"error_code":500,"description":"Internal server error"}`), nil)
|
||||
|
||||
bot := client.New("test:token", client.WithHTTPClient(m))
|
||||
params := &SendMessageParams{
|
||||
ChatID: ChatIDFromInt(123),
|
||||
Text: "test_value",
|
||||
}
|
||||
_, err := SendMessage(context.Background(), bot, params)
|
||||
require.Error(t, err)
|
||||
var ae *client.APIError
|
||||
require.ErrorAs(t, err, &ae)
|
||||
require.Equal(t, 500, ae.Code)
|
||||
require.True(t, ae.IsRetryable(), "5xx must be retryable")
|
||||
}
|
||||
|
||||
func Test_SendDocument_Success(t *testing.T) {
|
||||
m := &genTestMockDoer{}
|
||||
m.On("Do", mock.MatchedBy(func(r *http.Request) bool {
|
||||
return strings.HasSuffix(r.URL.Path, "/sendDocument")
|
||||
})).Return(genTestResp(200, `{"ok":true,"result":{}}`), nil)
|
||||
|
||||
bot := client.New("test:token", client.WithHTTPClient(m))
|
||||
params := &SendDocumentParams{
|
||||
ChatID: 42,
|
||||
Document: &InputFile{PathOrID: "file_id_test"},
|
||||
}
|
||||
_, err := SendDocument(context.Background(), bot, params)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func Test_SendDocument_APIError(t *testing.T) {
|
||||
m := &genTestMockDoer{}
|
||||
m.On("Do", mock.Anything).Return(
|
||||
genTestResp(200, `{"ok":false,"error_code":429,"description":"Too Many Requests","parameters":{"retry_after":1}}`), nil)
|
||||
|
||||
bot := client.New("test:token", client.WithHTTPClient(m))
|
||||
params := &SendDocumentParams{
|
||||
ChatID: 42,
|
||||
Document: &InputFile{PathOrID: "file_id_test"},
|
||||
}
|
||||
_, err := SendDocument(context.Background(), bot, params)
|
||||
require.Error(t, err)
|
||||
var ae *client.APIError
|
||||
require.ErrorAs(t, err, &ae)
|
||||
require.Equal(t, 429, ae.Code)
|
||||
require.True(t, ae.IsRetryable())
|
||||
}
|
||||
|
||||
func Test_SendDocument_NetworkError(t *testing.T) {
|
||||
m := &genTestMockDoer{}
|
||||
m.On("Do", mock.Anything).Return(nil, errors.New("dial tcp: timeout"))
|
||||
|
||||
bot := client.New("test:token", client.WithHTTPClient(m))
|
||||
params := &SendDocumentParams{
|
||||
ChatID: 42,
|
||||
Document: &InputFile{PathOrID: "file_id_test"},
|
||||
}
|
||||
_, err := SendDocument(context.Background(), bot, params)
|
||||
require.Error(t, err)
|
||||
var ne *client.NetworkError
|
||||
require.ErrorAs(t, err, &ne)
|
||||
}
|
||||
|
||||
func Test_SendDocument_ParseError(t *testing.T) {
|
||||
m := &genTestMockDoer{}
|
||||
m.On("Do", mock.Anything).Return(genTestResp(200, `not json`), nil)
|
||||
|
||||
bot := client.New("test:token", client.WithHTTPClient(m))
|
||||
params := &SendDocumentParams{
|
||||
ChatID: 42,
|
||||
Document: &InputFile{PathOrID: "file_id_test"},
|
||||
}
|
||||
_, err := SendDocument(context.Background(), bot, params)
|
||||
require.Error(t, err)
|
||||
var pe *client.ParseError
|
||||
require.ErrorAs(t, err, &pe)
|
||||
}
|
||||
|
||||
func Test_SendDocument_ContextCanceled(t *testing.T) {
|
||||
m := &genTestMockDoer{}
|
||||
m.On("Do", mock.Anything).Return(nil, context.Canceled).Maybe()
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel()
|
||||
|
||||
bot := client.New("test:token", client.WithHTTPClient(m))
|
||||
params := &SendDocumentParams{
|
||||
ChatID: 42,
|
||||
Document: &InputFile{PathOrID: "file_id_test"},
|
||||
}
|
||||
_, err := SendDocument(ctx, bot, params)
|
||||
require.Error(t, err)
|
||||
require.ErrorIs(t, err, context.Canceled)
|
||||
}
|
||||
|
||||
// Test_SendDocument_MissingRequiredFields exercises Telegram's server-side
|
||||
// validation: when a required field is omitted, Telegram returns 400 with
|
||||
// a description like "Bad Request: <field> is empty". The library must
|
||||
// surface this as *APIError with the ErrBadRequest sentinel.
|
||||
func Test_SendDocument_MissingRequiredFields(t *testing.T) {
|
||||
m := &genTestMockDoer{}
|
||||
m.On("Do", mock.Anything).Return(
|
||||
genTestResp(200, `{"ok":false,"error_code":400,"description":"Bad Request: chat_id is empty"}`), nil)
|
||||
|
||||
bot := client.New("test:token", client.WithHTTPClient(m))
|
||||
// Send a Params with all required fields zeroed — simulates a caller
|
||||
// that forgot to populate them. The bot library marshals as-is and
|
||||
// surfaces Telegram's 400 reply.
|
||||
_, err := SendDocument(context.Background(), bot, &SendDocumentParams{})
|
||||
require.Error(t, err)
|
||||
var ae *client.APIError
|
||||
require.ErrorAs(t, err, &ae)
|
||||
require.Equal(t, 400, ae.Code)
|
||||
require.True(t, errors.Is(err, client.ErrBadRequest))
|
||||
require.False(t, ae.IsRetryable())
|
||||
}
|
||||
|
||||
// Test_SendDocument_Forbidden exercises the 403 path (bot blocked by user,
|
||||
// removed from chat, etc.). The library must surface the ErrForbidden
|
||||
// sentinel.
|
||||
func Test_SendDocument_Forbidden(t *testing.T) {
|
||||
m := &genTestMockDoer{}
|
||||
m.On("Do", mock.Anything).Return(
|
||||
genTestResp(200, `{"ok":false,"error_code":403,"description":"Forbidden: bot was blocked by the user"}`), nil)
|
||||
|
||||
bot := client.New("test:token", client.WithHTTPClient(m))
|
||||
params := &SendDocumentParams{
|
||||
ChatID: 42,
|
||||
Document: &InputFile{PathOrID: "file_id_test"},
|
||||
}
|
||||
_, err := SendDocument(context.Background(), bot, params)
|
||||
require.Error(t, err)
|
||||
var ae *client.APIError
|
||||
require.ErrorAs(t, err, &ae)
|
||||
require.Equal(t, 403, ae.Code)
|
||||
require.True(t, errors.Is(err, client.ErrForbidden))
|
||||
require.False(t, ae.IsRetryable())
|
||||
}
|
||||
|
||||
// Test_SendDocument_ServerError exercises the 5xx path. The library must
|
||||
// classify these as retryable so RetryDoer / user retry logic kicks in.
|
||||
func Test_SendDocument_ServerError(t *testing.T) {
|
||||
m := &genTestMockDoer{}
|
||||
m.On("Do", mock.Anything).Return(
|
||||
genTestResp(200, `{"ok":false,"error_code":500,"description":"Internal server error"}`), nil)
|
||||
|
||||
bot := client.New("test:token", client.WithHTTPClient(m))
|
||||
params := &SendDocumentParams{
|
||||
ChatID: 42,
|
||||
Document: &InputFile{PathOrID: "file_id_test"},
|
||||
}
|
||||
_, err := SendDocument(context.Background(), bot, params)
|
||||
require.Error(t, err)
|
||||
var ae *client.APIError
|
||||
require.ErrorAs(t, err, &ae)
|
||||
require.Equal(t, 500, ae.Code)
|
||||
require.True(t, ae.IsRetryable(), "5xx must be retryable")
|
||||
}
|
||||
|
||||
func Test_GetUpdates_Success(t *testing.T) {
|
||||
m := &genTestMockDoer{}
|
||||
m.On("Do", mock.MatchedBy(func(r *http.Request) bool {
|
||||
return strings.HasSuffix(r.URL.Path, "/getUpdates")
|
||||
})).Return(genTestResp(200, `{"ok":true,"result":[]}`), nil)
|
||||
|
||||
bot := client.New("test:token", client.WithHTTPClient(m))
|
||||
params := &GetUpdatesParams{}
|
||||
_, err := GetUpdates(context.Background(), bot, params)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func Test_GetUpdates_APIError(t *testing.T) {
|
||||
m := &genTestMockDoer{}
|
||||
m.On("Do", mock.Anything).Return(
|
||||
genTestResp(200, `{"ok":false,"error_code":429,"description":"Too Many Requests","parameters":{"retry_after":1}}`), nil)
|
||||
|
||||
bot := client.New("test:token", client.WithHTTPClient(m))
|
||||
params := &GetUpdatesParams{}
|
||||
_, err := GetUpdates(context.Background(), bot, params)
|
||||
require.Error(t, err)
|
||||
var ae *client.APIError
|
||||
require.ErrorAs(t, err, &ae)
|
||||
require.Equal(t, 429, ae.Code)
|
||||
require.True(t, ae.IsRetryable())
|
||||
}
|
||||
|
||||
func Test_GetUpdates_NetworkError(t *testing.T) {
|
||||
m := &genTestMockDoer{}
|
||||
m.On("Do", mock.Anything).Return(nil, errors.New("dial tcp: timeout"))
|
||||
|
||||
bot := client.New("test:token", client.WithHTTPClient(m))
|
||||
params := &GetUpdatesParams{}
|
||||
_, err := GetUpdates(context.Background(), bot, params)
|
||||
require.Error(t, err)
|
||||
var ne *client.NetworkError
|
||||
require.ErrorAs(t, err, &ne)
|
||||
}
|
||||
|
||||
func Test_GetUpdates_ParseError(t *testing.T) {
|
||||
m := &genTestMockDoer{}
|
||||
m.On("Do", mock.Anything).Return(genTestResp(200, `not json`), nil)
|
||||
|
||||
bot := client.New("test:token", client.WithHTTPClient(m))
|
||||
params := &GetUpdatesParams{}
|
||||
_, err := GetUpdates(context.Background(), bot, params)
|
||||
require.Error(t, err)
|
||||
var pe *client.ParseError
|
||||
require.ErrorAs(t, err, &pe)
|
||||
}
|
||||
|
||||
func Test_GetUpdates_ContextCanceled(t *testing.T) {
|
||||
m := &genTestMockDoer{}
|
||||
m.On("Do", mock.Anything).Return(nil, context.Canceled).Maybe()
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel()
|
||||
|
||||
bot := client.New("test:token", client.WithHTTPClient(m))
|
||||
params := &GetUpdatesParams{}
|
||||
_, err := GetUpdates(ctx, bot, params)
|
||||
require.Error(t, err)
|
||||
require.ErrorIs(t, err, context.Canceled)
|
||||
}
|
||||
|
||||
// Test_GetUpdates_MissingRequiredFields exercises Telegram's server-side
|
||||
// validation: when a required field is omitted, Telegram returns 400 with
|
||||
// a description like "Bad Request: <field> is empty". The library must
|
||||
// surface this as *APIError with the ErrBadRequest sentinel.
|
||||
func Test_GetUpdates_MissingRequiredFields(t *testing.T) {
|
||||
m := &genTestMockDoer{}
|
||||
m.On("Do", mock.Anything).Return(
|
||||
genTestResp(200, `{"ok":false,"error_code":400,"description":"Bad Request: chat_id is empty"}`), nil)
|
||||
|
||||
bot := client.New("test:token", client.WithHTTPClient(m))
|
||||
// Send a Params with all required fields zeroed — simulates a caller
|
||||
// that forgot to populate them. The bot library marshals as-is and
|
||||
// surfaces Telegram's 400 reply.
|
||||
_, err := GetUpdates(context.Background(), bot, &GetUpdatesParams{})
|
||||
require.Error(t, err)
|
||||
var ae *client.APIError
|
||||
require.ErrorAs(t, err, &ae)
|
||||
require.Equal(t, 400, ae.Code)
|
||||
require.True(t, errors.Is(err, client.ErrBadRequest))
|
||||
require.False(t, ae.IsRetryable())
|
||||
}
|
||||
|
||||
// Test_GetUpdates_Forbidden exercises the 403 path (bot blocked by user,
|
||||
// removed from chat, etc.). The library must surface the ErrForbidden
|
||||
// sentinel.
|
||||
func Test_GetUpdates_Forbidden(t *testing.T) {
|
||||
m := &genTestMockDoer{}
|
||||
m.On("Do", mock.Anything).Return(
|
||||
genTestResp(200, `{"ok":false,"error_code":403,"description":"Forbidden: bot was blocked by the user"}`), nil)
|
||||
|
||||
bot := client.New("test:token", client.WithHTTPClient(m))
|
||||
params := &GetUpdatesParams{}
|
||||
_, err := GetUpdates(context.Background(), bot, params)
|
||||
require.Error(t, err)
|
||||
var ae *client.APIError
|
||||
require.ErrorAs(t, err, &ae)
|
||||
require.Equal(t, 403, ae.Code)
|
||||
require.True(t, errors.Is(err, client.ErrForbidden))
|
||||
require.False(t, ae.IsRetryable())
|
||||
}
|
||||
|
||||
// Test_GetUpdates_ServerError exercises the 5xx path. The library must
|
||||
// classify these as retryable so RetryDoer / user retry logic kicks in.
|
||||
func Test_GetUpdates_ServerError(t *testing.T) {
|
||||
m := &genTestMockDoer{}
|
||||
m.On("Do", mock.Anything).Return(
|
||||
genTestResp(200, `{"ok":false,"error_code":500,"description":"Internal server error"}`), nil)
|
||||
|
||||
bot := client.New("test:token", client.WithHTTPClient(m))
|
||||
params := &GetUpdatesParams{}
|
||||
_, err := GetUpdates(context.Background(), bot, params)
|
||||
require.Error(t, err)
|
||||
var ae *client.APIError
|
||||
require.ErrorAs(t, err, &ae)
|
||||
require.Equal(t, 500, ae.Code)
|
||||
require.True(t, ae.IsRetryable(), "5xx must be retryable")
|
||||
}
|
||||
Vendored
+75
@@ -0,0 +1,75 @@
|
||||
// Code generated by cmd/genapi. DO NOT EDIT.
|
||||
|
||||
//go:build !ignore_autogenerated
|
||||
|
||||
// Package api contains the Telegram Bot API object types and method
|
||||
// wrappers, generated from the live documentation by cmd/genapi.
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/goccy/go-json"
|
||||
"io"
|
||||
)
|
||||
|
||||
var _ = io.Discard // keep import even if no fields use io
|
||||
var _ = json.Marshal // keep import for UnmarshalXxx helpers
|
||||
var _ = fmt.Errorf // keep import for UnmarshalXxx helpers
|
||||
|
||||
// This object represents a Telegram user or bot.
|
||||
type User struct {
|
||||
// Unique identifier.
|
||||
ID int64 `json:"id"`
|
||||
// True, if this user is a bot.
|
||||
IsBot bool `json:"is_bot"`
|
||||
// User's or bot's first name.
|
||||
FirstName string `json:"first_name"`
|
||||
// Optional. User's or bot's last name.
|
||||
LastName string `json:"last_name,omitempty"`
|
||||
}
|
||||
|
||||
// ChatMember is a union type. The following concrete variants implement
|
||||
// it:
|
||||
// - ChatMemberOwner
|
||||
// - ChatMemberAdministrator
|
||||
//
|
||||
// This object contains information about one member of a chat. Currently, the following 6 types of chat members are supported:
|
||||
type ChatMember interface{ isChatMember() }
|
||||
|
||||
// isChatMember is the marker method that makes ChatMemberOwner implement ChatMember.
|
||||
func (*ChatMemberOwner) isChatMember() {}
|
||||
|
||||
// isChatMember is the marker method that makes ChatMemberAdministrator implement ChatMember.
|
||||
func (*ChatMemberAdministrator) isChatMember() {}
|
||||
|
||||
// UnmarshalChatMember decodes a ChatMember from JSON by inspecting the
|
||||
// "status" field and dispatching to the correct concrete type.
|
||||
func UnmarshalChatMember(data []byte) (ChatMember, error) {
|
||||
var probe struct {
|
||||
V string `json:"status"`
|
||||
}
|
||||
if err := json.Unmarshal(data, &probe); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var v ChatMember
|
||||
switch probe.V {
|
||||
case "administrator":
|
||||
v = &ChatMemberAdministrator{}
|
||||
case "creator":
|
||||
v = &ChatMemberOwner{}
|
||||
case "kicked":
|
||||
v = &ChatMemberBanned{}
|
||||
case "left":
|
||||
v = &ChatMemberLeft{}
|
||||
case "member":
|
||||
v = &ChatMemberMember{}
|
||||
case "restricted":
|
||||
v = &ChatMemberRestricted{}
|
||||
default:
|
||||
return nil, fmt.Errorf("ChatMember: unknown status %q", probe.V)
|
||||
}
|
||||
if err := json.Unmarshal(data, v); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
snapshot_2026-05-08.html
|
||||
Vendored
+68
@@ -0,0 +1,68 @@
|
||||
<!DOCTYPE html>
|
||||
<html><head><meta charset="utf-8"><title>fixture</title></head><body>
|
||||
<div id="dev_page_content">
|
||||
|
||||
<h3>Recent changes</h3>
|
||||
<h4>Bot API 7.10</h4>
|
||||
<p>Test fixture; not a real release.</p>
|
||||
|
||||
<h3>Available types</h3>
|
||||
|
||||
<h4><a class="anchor" href="#user" name="user"></a>User</h4>
|
||||
<p>This object represents a Telegram user or bot.</p>
|
||||
<table class="table">
|
||||
<thead><tr><th>Field</th><th>Type</th><th>Description</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>id</td><td>Integer</td><td>Unique identifier.</td></tr>
|
||||
<tr><td>is_bot</td><td>Boolean</td><td>True, if this user is a bot.</td></tr>
|
||||
<tr><td>first_name</td><td>String</td><td>User's or bot's first name.</td></tr>
|
||||
<tr><td>last_name</td><td>String</td><td><em>Optional</em>. User's or bot's last name.</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h4><a class="anchor" href="#chatmember" name="chatmember"></a>ChatMember</h4>
|
||||
<p>This object contains information about one member of a chat.
|
||||
Currently, the following 6 types of chat members are supported:</p>
|
||||
<ul>
|
||||
<li><a href="#chatmemberowner">ChatMemberOwner</a></li>
|
||||
<li><a href="#chatmemberadministrator">ChatMemberAdministrator</a></li>
|
||||
</ul>
|
||||
|
||||
<h3>Available methods</h3>
|
||||
|
||||
<h4><a class="anchor" href="#getme" name="getme"></a>getMe</h4>
|
||||
<p>A simple method for testing your bot's authentication token. Requires
|
||||
no parameters. Returns basic information about the bot in form of a <a href="#user">User</a> object.</p>
|
||||
|
||||
<h4><a class="anchor" href="#sendmessage" name="sendmessage"></a>sendMessage</h4>
|
||||
<p>Use this method to send text messages. On success, the sent <a href="#message">Message</a> is returned.</p>
|
||||
<table class="table">
|
||||
<thead><tr><th>Parameter</th><th>Type</th><th>Required</th><th>Description</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>chat_id</td><td>Integer or String</td><td>Yes</td><td>Unique identifier for the target chat.</td></tr>
|
||||
<tr><td>text</td><td>String</td><td>Yes</td><td>Text of the message to be sent.</td></tr>
|
||||
<tr><td>parse_mode</td><td>String</td><td>Optional</td><td>Mode for parsing entities in the message text.</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h4><a class="anchor" href="#senddocument" name="senddocument"></a>sendDocument</h4>
|
||||
<p>Use this method to send general files. On success, the sent <a href="#message">Message</a> is returned.</p>
|
||||
<table class="table">
|
||||
<thead><tr><th>Parameter</th><th>Type</th><th>Required</th><th>Description</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>chat_id</td><td>Integer</td><td>Yes</td><td>Unique identifier for the target chat.</td></tr>
|
||||
<tr><td>document</td><td><a href="#inputfile">InputFile</a> or String</td><td>Yes</td><td>File to send.</td></tr>
|
||||
<tr><td>caption</td><td>String</td><td>Optional</td><td>Document caption.</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h4><a class="anchor" href="#getupdates" name="getupdates"></a>getUpdates</h4>
|
||||
<p>Use this method to receive incoming updates using long polling. Returns an Array of <a href="#update">Update</a> objects.</p>
|
||||
<table class="table">
|
||||
<thead><tr><th>Parameter</th><th>Type</th><th>Required</th><th>Description</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>limit</td><td>Integer</td><td>Optional</td><td>Limits the number of updates to be retrieved.</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div></body></html>
|
||||
+19872
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user