Typed API services
Messages, chats, users, polls and updates have dedicated services behind one client.
ymsdkv0.2.0
Yandex Messenger Bot API client
Send messages, create polls and manage chats through typed Go services. Configure retries and rate limits, then receive updates through polling or webhooks.
Go · MIT · Last update
cs := client.New(ym.Config{
Token: os.Getenv("YM_TOKEN"),
})
msg, err := cs.Messages.SendToChat(
ctx, "chat-id", "Deployment finished", nil,
)
if err != nil {
return err
}
fmt.Println(msg.ID)
ymsdk
Messages, chats, users, polls and updates have dedicated services behind one client.
Configure backoff and Retry-After handling. Supported send methods use payload IDs to reduce duplicate retries.
Receive updates through a polling loop or a webhook handler that deduplicates repeated deliveries.
Yandex Messenger Bot API client
Read the bot token from your environment and configure the retry policy for your workflow.
Start with a deployment notification, then add updates, polls or chat management through the same client.
01 / Install
go get github.com/rekurt/ymsdk02 / README
Setup, examples and API reference.
Lightweight Go client for Yandex Messenger Bot API with typed models, built-in retry, and services for all 28 API methods. Docs: https://pkg.go.dev/github.com/rekurt/ymsdk
ChatID, UserLogin, MessageID and other distinct types prevent mix-ups at compile timepayload_id idempotency key on sendText, sendSticker, sendSystemMessage and createPoll, so a retried send cannot deliver the message twice. Every other send — the uploads (sendFile, sendImage, sendGallery) and the by-file-id resends (shareFile, shareImage, shareGallery) — has no idempotency key in the API, so retrying one can duplicate itRetry-After headerszap with HTTP request/response inspectiongo.uber.org/zapgo get github.com/rekurt/ymsdk
package main
import (
"context"
"fmt"
"os"
"github.com/rekurt/ymsdk/client"
"github.com/rekurt/ymsdk/client/ym"
"github.com/rekurt/ymsdk/client/ym/ymerrors"
)
func main() {
cs := client.New(ym.Config{
Token: os.Getenv("YM_TOKEN"),
ErrorHandling: ymerrors.ErrorHandlingConfig{
RetryStrategy: ymerrors.RetryStrategy{MaxAttempts: 3, RetryNetwork: true},
RateLimitHandling: ymerrors.RateLimitHandling{UseRetryAfter: true},
},
})
msg, err := cs.Messages.SendToChat(context.Background(), "chat-id", "hello", nil)
if err != nil {
fmt.Println("error:", err)
return
}
fmt.Println("sent message:", msg.ID)
}
cl := ym.NewClient(ym.Config{Token: os.Getenv("YM_TOKEN")})
msgSvc := messages.NewService(cl)
pollSvc := polls.NewService(cl)
msg, _ := msgSvc.SendToChat(ctx, "chat-id", "hello", nil)
client/
├── sdk.go # YMClient — aggregator with all services
└── ym/ # Core SDK
├── client.go # HTTP client with retry/rate-limit logic
├── types.go # Shared types (Chat, Message, Update, …)
├── ptr.go # ym.Ptr[T] helper for optional fields
├── validate.go # Shared recipient validation
├── ymerrors/ # Error types and configuration
├── messages/ # Text, files, images, galleries, delete, getFile
├── chats/ # Create chats/channels, manage members
├── users/ # User chat/call deep links
├── polls/ # Polls: create, results, voters, GetAllVoters
├── updates/ # getUpdates, GetUpdates, and PollLoop
└── self/ # Bot webhook_url management
middleware/ # zap-based logging
├── logging.go # LogError, LogUpdateWithRawData, WithRequestID
├── debug.go # DebugLogger with levels (Silent → Debug)
└── http_logger.go # HTTP wrapper for request/response logging
| Service | Description |
|---|---|
cs.Messages |
Text and edits, files, images, galleries, stickers, system messages, reactions, pinning, typing indicator, forwarding, delete, download |
cs.Chats |
Create chats and channels, list chats, chat info, members, membership management |
cs.Users |
Get chat_link / call_link by login |
cs.Polls |
Create polls, results, paginated voters, GetAllVoters |
cs.Updates |
getUpdates, resilient Run loop, deduplicating webhook handler |
cs.Self |
Bot info, webhook_url, get_reactions / get_members_changed flags |
All 28 methods described in the Bot API documentation are implemented.
| Domain | Methods |
|---|---|
| Messages | sendText (edits via message_id), sendFile, sendImage, sendGallery, sendSticker, sendSystemMessage, sendTyping, shareFile, shareImage, shareGallery, delete, pin, unpin, sendReaction, getReactions, getFile, getUpdates |
| Chats | create, get, getChat, getMembers, updateMembers |
| Polls | createPoll, getResults, getVoters |
| Bot | self/get, self/update |
| Users | getUserLink |
Convenience aggregator client.YMClient with prebuilt services:
client.New(cfg) — create with new HTTP clientclient.Wrap(cl) — wrap existing ym.Clientvar apiErr *ymerrors.APIError
if errors.As(err, &apiErr) {
fmt.Printf("kind=%d http=%d desc=%s request_id=%s\n",
apiErr.Kind, apiErr.HTTPStatus, apiErr.Description, apiErr.RequestID)
if errors.Is(err, ymerrors.ErrRateLimited) && apiErr.RetryAfter > 0 {
time.Sleep(apiErr.RetryAfter)
}
}
*ymerrors.APIError (use errors.As).errors.Is(err, ymerrors.ErrRateLimited) + RetryAfter.ErrInvalidToken (403) / ErrUnauthorized (401).KindNetwork (5xx) / net.Error when RetryNetwork enabled.cfg := ym.Config{
BaseURL: "", // defaults to production endpoint
Token: os.Getenv("YM_TOKEN"),
ErrorHandling: ymerrors.ErrorHandlingConfig{
RetryStrategy: ymerrors.RetryStrategy{
MaxAttempts: 3,
InitialBackoff: 500 * time.Millisecond,
MaxBackoff: 10 * time.Second,
RetryNetwork: true,
RetryHTTP: []int{500, 502, 503, 504},
},
RateLimitHandling: ymerrors.RateLimitHandling{
UseRetryAfter: true,
DefaultBackoff: time.Second,
},
},
UpdatesMode: ymerrors.UpdatesModePolling, // "polling" or "webhook"
}
Inspect raw HTTP request/response bodies with middleware:
logger, _ := zap.NewDevelopmentConfig().Build()
debugLogger := middleware.NewDebugLogger(logger, middleware.LogLevelDebug)
loggedHTTP := middleware.NewHTTPLogger(&http.Client{Timeout: 15 * time.Second}, debugLogger)
ymClient := ym.NewClientWithHTTP(cfg, loggedHTTP)
cs := client.Wrap(ymClient)
See middleware/README.md and examples/debug_logger for details.
| Example | Description |
|---|---|
examples/basic_send |
Send text to chat/login, reply-to, mark-important, error handling |
examples/poller |
Continuous polling via PollLoop, handles text/files/stickers/forwards |
examples/poll_bot |
Create poll, GetResults, GetAllVoters, read updates |
examples/webhook |
HTTP webhook receiver with secret validation, graceful shutdown, echo bot |
examples/debug_logger |
HTTP request/response logging, handling updates without messages |
examples/integration |
End-to-end script exercising all SDK methods (configure via env) |
# Send a message
cd examples/basic_send
YM_TOKEN=... go run . -chat "chat-id" -text "hello"
# Poll for updates
cd examples/poller
YM_TOKEN=... go run .
# Poll bot
cd examples/poll_bot
YM_TOKEN=... YM_CHAT_ID=... go run .
# Webhook server
cd examples/webhook
YM_TOKEN=... YM_WEBHOOK_SECRET=... YM_PORT=8080 go run .
# Debug logging
cd examples/debug_logger
YM_TOKEN=... go run .
# Full integration
cd examples/integration
YM_TOKEN=... YM_CHAT_ID=... YM_LOGIN=... go run .
The repository ships a skill so that Claude, Codex, Cursor, Copilot, Gemini and
Windsurf write correct code against this SDK. It covers the API, and more
usefully the places where obvious-looking code compiles and then fails in
production: retries are off by default, payload_id carries idempotency,
Update.Images has the nested [][]ym.Image shape, webhooks get a one-second
budget, and getUpdates permanently erases updates below the offset.
| File | Purpose |
|---|---|
skills/ymsdk/SKILL.md |
Skill for Claude Code and claude.ai |
skills/ymsdk/references/reference.md |
Full reference — the single source of truth |
skills/ymsdk/references/recipes.md |
Complete programs: echo bot, button bot, webhook service |
AGENTS.md |
Codex, Cursor, Jules and anything else reading AGENTS.md |
GEMINI.md |
Gemini CLI |
.cursor/rules/ymsdk.mdc |
Cursor rules |
.github/copilot-instructions.md |
GitHub Copilot |
.windsurfrules |
Windsurf |
To use it in your own project, copy the skill directory:
mkdir -p .claude/skills
cp -r "$(go env GOMODCACHE)"/github.com/rekurt/ymsdk@*/skills/ymsdk .claude/skills/
Every program in recipes.md is compile-checked, and the per-platform files
point at one shared reference so they cannot drift apart from the code.
This project follows Semantic Versioning. To install a specific version:
go get github.com/rekurt/ymsdk@v0.2.0
# Run all tests
go test ./...
# Lint (50+ linters)
golangci-lint run --config .golangci.yml
ymsdk
03 / All projects
Libraries, command-line tools and applications.