REPOSITORY / ScuroNeko/Laniakea
Compare commits
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6b9075c722
|
||
|
|
0b1a58a514
|
||
|
|
c59dd1fe8e
|
||
|
|
2fc171d9a3
|
||
|
|
4ebe76dd4a
|
||
|
|
1e043da05d
|
||
|
|
389ec9f9d7
|
@@ -28,6 +28,12 @@ A lightweight, easy-to-use, and performant Telegram Bot API wrapper for Go. It s
|
|||||||
go get git.nix13.pw/scuroneko/laniakea
|
go get git.nix13.pw/scuroneko/laniakea
|
||||||
```
|
```
|
||||||
|
|
||||||
|
or
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go get github.com/scuroneko/laniakea
|
||||||
|
```
|
||||||
|
|
||||||
## 🚀 Quick Start (with step-by-step explanation)
|
## 🚀 Quick Start (with step-by-step explanation)
|
||||||
|
|
||||||
Here is a minimal echo/ping bot example with detailed comments.
|
Here is a minimal echo/ping bot example with detailed comments.
|
||||||
@@ -129,13 +135,22 @@ Provides access to the incoming message and useful reply methods:
|
|||||||
- `Keyboard(text string, keyboard *InlineKeyboard) *AnswerMessage`: Sends a message with parse_mode none and inline keyboard.
|
- `Keyboard(text string, keyboard *InlineKeyboard) *AnswerMessage`: Sends a message with parse_mode none and inline keyboard.
|
||||||
- `KeyboardMarkdown(text string, keyboard *InlineKeyboard) *AnswerMessage`: Sends a message formatted with MarkdownV2 (you handle escaping) and inline keyboard.
|
- `KeyboardMarkdown(text string, keyboard *InlineKeyboard) *AnswerMessage`: Sends a message formatted with MarkdownV2 (you handle escaping) and inline keyboard.
|
||||||
- `AnswerPhoto(photoId, text string) *AnswerMessage`: Sends a message with photo with parse_mode none.
|
- `AnswerPhoto(photoId, text string) *AnswerMessage`: Sends a message with photo with parse_mode none.
|
||||||
- `AnswerPhotoMarkdown(photoId, text string) *AnswerMessage`: Sends a message formatted with MarkdownV2 (you handle escaping) with.
|
- `AnswerPhotoMarkdown(photoId, text string) *AnswerMessage`: Sends a photo with MarkdownV2 caption (you handle escaping).
|
||||||
- `EditCallback(text string)`: Edits message with parse_mode none after clicking inline button.
|
- `EditCallback(text string)`: Edits message with parse_mode none after clicking inline button.
|
||||||
- `EditCallbackMarkdown(text string)`: Edits a message formatted with MarkdownV2 (you handle escaping) after clicking inline button.
|
- `EditCallbackMarkdown(text string)`: Edits a message formatted with MarkdownV2 (you handle escaping) after clicking inline button.
|
||||||
- `SendChatAction(action string)`: Sends a “typing”, “uploading photo”, etc., action.
|
- `SendAction(action tgapi.ChatActionType)`: Sends a “typing”, “uploading photo”, etc., action.
|
||||||
- Fields: `Text`, `Args`, `From`, `Chat`, `Msg`, etc.
|
- Fields: `Text`, `Args`, `From`, `FromID`, `Msg`, `InlineMsgId`, `CallbackQueryId`, etc.
|
||||||
- And more methods and fields!
|
- And more methods and fields!
|
||||||
|
|
||||||
|
### tgapi: API and Uploader
|
||||||
|
|
||||||
|
`tgapi` provides two clients:
|
||||||
|
|
||||||
|
- `API` for JSON requests (e.g., `SendMessage`, `EditMessageText`, methods using file_id/URL).
|
||||||
|
- `Uploader` for multipart uploads (e.g., `SendPhoto`, `SendDocument`, `SendVideo` with binary files).
|
||||||
|
|
||||||
|
This split keeps method intent explicit: JSON-only calls go through `API`, file uploads go through `Uploader`.
|
||||||
|
|
||||||
### Database Context
|
### Database Context
|
||||||
|
|
||||||
The `T` in `NewBot[T]` is a powerful feature. You can pass any type (like a database connection pool), and it will be available in every command and middleware handler.
|
The `T` in `NewBot[T]` is a powerful feature. You can pass any type (like a database connection pool), and it will be available in every command and middleware handler.
|
||||||
@@ -193,7 +208,7 @@ func adminOnlyMiddleware(ctx *laniakea.MsgContext, db *MyDB) bool {
|
|||||||
- Middleware can modify the MsgContext (e.g., add custom fields) before the command runs.
|
- Middleware can modify the MsgContext (e.g., add custom fields) before the command runs.
|
||||||
|
|
||||||
## ⚙️ Advanced Configuration
|
## ⚙️ Advanced Configuration
|
||||||
- **Inline Keyboards**: Build keyboards using laniakea.NewKeyboard().
|
- **Inline Keyboards**: Build keyboards using `laniakea.NewInlineKeyboardJson`, `laniakea.NewInlineKeyboardBase64`, or `laniakea.NewInlineKeyboard`.
|
||||||
- **Rate Limiting**: Pass a configured utils.RateLimiter via BotOpts to handle Telegram's rate limits gracefully.
|
- **Rate Limiting**: Pass a configured utils.RateLimiter via BotOpts to handle Telegram's rate limits gracefully.
|
||||||
- **Custom HTTP Client**: Provide your own http.Client in BotOpts for fine-tuned control.
|
- **Custom HTTP Client**: Provide your own http.Client in BotOpts for fine-tuned control.
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,12 @@
|
|||||||
go get git.nix13.pw/scuroneko/laniakea
|
go get git.nix13.pw/scuroneko/laniakea
|
||||||
```
|
```
|
||||||
|
|
||||||
|
или
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go get github.com/scuroneko/laniakea
|
||||||
|
```
|
||||||
|
|
||||||
## 🚀 Быстрый старт (с пошаговыми комментариями)
|
## 🚀 Быстрый старт (с пошаговыми комментариями)
|
||||||
Вот минимальный пример бота "echo/ping" с подробными комментариями.
|
Вот минимальный пример бота "echo/ping" с подробными комментариями.
|
||||||
|
|
||||||
|
|||||||
@@ -2,11 +2,11 @@ package laniakea
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
|
||||||
|
|
||||||
"git.nix13.pw/scuroneko/extypes"
|
"git.nix13.pw/scuroneko/extypes"
|
||||||
"git.nix13.pw/scuroneko/laniakea/tgapi"
|
"git.nix13.pw/scuroneko/laniakea/tgapi"
|
||||||
@@ -81,6 +81,8 @@ type Bot[T DbContext] struct {
|
|||||||
updateOffset int // Last processed update ID
|
updateOffset int // Last processed update ID
|
||||||
updateTypes []tgapi.UpdateType // Types of updates to fetch
|
updateTypes []tgapi.UpdateType // Types of updates to fetch
|
||||||
updateQueue chan *tgapi.Update // Internal queue for processing updates
|
updateQueue chan *tgapi.Update // Internal queue for processing updates
|
||||||
|
runnerOnceWG sync.WaitGroup // Tracks one-time async runners
|
||||||
|
runnerBgWG sync.WaitGroup // Tracks background async runners
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBot creates and initializes a new Bot instance using the provided BotOpts.
|
// NewBot creates and initializes a new Bot instance using the provided BotOpts.
|
||||||
@@ -107,11 +109,13 @@ func NewBot[T any](opts *BotOpts) *Bot[T] {
|
|||||||
// limiter = utils.NewRateLimiter()
|
// limiter = utils.NewRateLimiter()
|
||||||
//}
|
//}
|
||||||
limiter := utils.NewRateLimiter()
|
limiter := utils.NewRateLimiter()
|
||||||
|
limiter.SetGlobalRate(opts.RateLimit)
|
||||||
|
|
||||||
apiOpts := tgapi.NewAPIOpts(opts.Token).
|
apiOpts := tgapi.NewAPIOpts(opts.Token).
|
||||||
SetAPIUrl(opts.APIUrl).
|
SetAPIUrl(opts.APIUrl).
|
||||||
UseTestServer(opts.UseTestServer).
|
UseTestServer(opts.UseTestServer).
|
||||||
SetLimiter(limiter)
|
SetLimiter(limiter).
|
||||||
|
SetLimiterDrop(opts.DropRLOverflow)
|
||||||
api := tgapi.NewAPI(apiOpts)
|
api := tgapi.NewAPI(apiOpts)
|
||||||
uploader := tgapi.NewUploader(api)
|
uploader := tgapi.NewUploader(api)
|
||||||
|
|
||||||
@@ -137,7 +141,7 @@ func NewBot[T any](opts *BotOpts) *Bot[T] {
|
|||||||
prefixes: prefixes,
|
prefixes: prefixes,
|
||||||
token: opts.Token,
|
token: opts.Token,
|
||||||
plugins: make([]Plugin[T], 0),
|
plugins: make([]Plugin[T], 0),
|
||||||
updateTypes: make([]tgapi.UpdateType, 0),
|
updateTypes: append([]tgapi.UpdateType{}, opts.UpdateTypes...),
|
||||||
runners: make([]Runner[T], 0),
|
runners: make([]Runner[T], 0),
|
||||||
extraLoggers: make([]*slog.Logger, 0),
|
extraLoggers: make([]*slog.Logger, 0),
|
||||||
l10n: &L10n{},
|
l10n: &L10n{},
|
||||||
@@ -170,7 +174,7 @@ func NewBot[T any](opts *BotOpts) *Bot[T] {
|
|||||||
return bot
|
return bot
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close gracefully shuts down the bot.
|
// Close gracefully shuts down bot-owned resources.
|
||||||
//
|
//
|
||||||
// Closes:
|
// Closes:
|
||||||
// - Uploader (waits for pending uploads)
|
// - Uploader (waits for pending uploads)
|
||||||
@@ -178,23 +182,35 @@ func NewBot[T any](opts *BotOpts) *Bot[T] {
|
|||||||
// - RequestLogger (if enabled)
|
// - RequestLogger (if enabled)
|
||||||
// - Main logger
|
// - Main logger
|
||||||
//
|
//
|
||||||
// Returns the first error encountered, if any.
|
// RunWithContext does not call Close automatically. The caller is responsible
|
||||||
|
// for invoking Close after RunWithContext returns to release these resources.
|
||||||
|
//
|
||||||
|
// Returns a joined error containing all shutdown failures, if any.
|
||||||
func (bot *Bot[T]) Close() error {
|
func (bot *Bot[T]) Close() error {
|
||||||
|
var e []error
|
||||||
|
|
||||||
if err := bot.uploader.Close(); err != nil {
|
if err := bot.uploader.Close(); err != nil {
|
||||||
bot.logger.Errorln(err)
|
bot.logger.Errorln(err)
|
||||||
|
e = append(e, err)
|
||||||
|
}
|
||||||
|
if _, err := bot.api.Close(); err != nil {
|
||||||
|
bot.logger.Errorln(err)
|
||||||
|
e = append(e, err)
|
||||||
}
|
}
|
||||||
if err := bot.api.CloseApi(); err != nil {
|
if err := bot.api.CloseApi(); err != nil {
|
||||||
bot.logger.Errorln(err)
|
bot.logger.Errorln(err)
|
||||||
|
e = append(e, err)
|
||||||
}
|
}
|
||||||
if bot.RequestLogger != nil {
|
if bot.RequestLogger != nil {
|
||||||
if err := bot.RequestLogger.Close(); err != nil {
|
if err := bot.RequestLogger.Close(); err != nil {
|
||||||
bot.logger.Errorln(err)
|
bot.logger.Errorln(err)
|
||||||
|
e = append(e, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := bot.logger.Close(); err != nil {
|
if err := bot.logger.Close(); err != nil {
|
||||||
return err
|
e = append(e, err)
|
||||||
}
|
}
|
||||||
return nil
|
return errors.Join(e...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// initLoggers configures the main and optional request loggers.
|
// initLoggers configures the main and optional request loggers.
|
||||||
@@ -285,9 +301,9 @@ func (bot *Bot[T]) UpdateTypes(t ...tgapi.UpdateType) *Bot[T] {
|
|||||||
return bot
|
return bot
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetPayloadType sets the type, that bot will use for payload
|
// SetPayloadType sets the payload encoding type used for callback data.
|
||||||
// json - string `{"cmd": "command", "args": [...]}
|
// JSON stores payload as a string: `{"cmd":"command","args":[...]}`.
|
||||||
// base64 - same json, but encoded in base64 string
|
// Base64 stores the same JSON encoded as a Base64URL string.
|
||||||
func (bot *Bot[T]) SetPayloadType(t BotPayloadType) *Bot[T] {
|
func (bot *Bot[T]) SetPayloadType(t BotPayloadType) *Bot[T] {
|
||||||
bot.payloadType = t
|
bot.payloadType = t
|
||||||
return bot
|
return bot
|
||||||
@@ -309,7 +325,7 @@ func (bot *Bot[T]) AddPrefixes(prefixes ...string) *Bot[T] {
|
|||||||
|
|
||||||
// ErrorTemplate sets the format string for error messages sent to users.
|
// ErrorTemplate sets the format string for error messages sent to users.
|
||||||
// Use "%s" to insert the error message.
|
// Use "%s" to insert the error message.
|
||||||
// Example: "❌ Error: %s" → "❌ Error: Command not found"
|
// Example: "❌ Error: %s" → "❌ Error: Command not found".
|
||||||
func (bot *Bot[T]) ErrorTemplate(s string) *Bot[T] {
|
func (bot *Bot[T]) ErrorTemplate(s string) *Bot[T] {
|
||||||
bot.errorTemplate = s
|
bot.errorTemplate = s
|
||||||
return bot
|
return bot
|
||||||
@@ -408,6 +424,7 @@ func (bot *Bot[T]) AddRunner(runner Runner[T]) *Bot[T] {
|
|||||||
func (bot *Bot[T]) AddL10n(l *L10n) *Bot[T] {
|
func (bot *Bot[T]) AddL10n(l *L10n) *Bot[T] {
|
||||||
if l == nil {
|
if l == nil {
|
||||||
bot.logger.Warn("AddL10n called with nil L10n; localization will be disabled")
|
bot.logger.Warn("AddL10n called with nil L10n; localization will be disabled")
|
||||||
|
return bot
|
||||||
}
|
}
|
||||||
bot.l10n = l
|
bot.l10n = l
|
||||||
return bot
|
return bot
|
||||||
@@ -448,7 +465,10 @@ func (bot *Bot[T]) AddDatabaseLoggerWriter(writer DbLogger[T]) *Bot[T] {
|
|||||||
// The context controls graceful shutdown. When canceled, the bot:
|
// The context controls graceful shutdown. When canceled, the bot:
|
||||||
// - Stops polling for new updates
|
// - Stops polling for new updates
|
||||||
// - Finishes processing currently queued updates
|
// - Finishes processing currently queued updates
|
||||||
// - Closes all resources (API, uploader, loggers)
|
// - Waits for registered runners to exit
|
||||||
|
//
|
||||||
|
// RunWithContext does not close API, uploader, or logger resources on return.
|
||||||
|
// The caller must invoke Close after RunWithContext finishes.
|
||||||
//
|
//
|
||||||
// Example:
|
// Example:
|
||||||
//
|
//
|
||||||
@@ -456,6 +476,7 @@ func (bot *Bot[T]) AddDatabaseLoggerWriter(writer DbLogger[T]) *Bot[T] {
|
|||||||
// go bot.RunWithContext(ctx)
|
// go bot.RunWithContext(ctx)
|
||||||
// // ... later ...
|
// // ... later ...
|
||||||
// cancel() // triggers graceful shutdown
|
// cancel() // triggers graceful shutdown
|
||||||
|
// _ = bot.Close()
|
||||||
func (bot *Bot[T]) RunWithContext(ctx context.Context) {
|
func (bot *Bot[T]) RunWithContext(ctx context.Context) {
|
||||||
if len(bot.prefixes) == 0 {
|
if len(bot.prefixes) == 0 {
|
||||||
bot.logger.Fatalln("no prefixes defined")
|
bot.logger.Fatalln("no prefixes defined")
|
||||||
@@ -484,15 +505,17 @@ func (bot *Bot[T]) RunWithContext(ctx context.Context) {
|
|||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return
|
return
|
||||||
default:
|
default:
|
||||||
updates, err := bot.Updates()
|
updates, err := bot.Updates(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if errors.Is(err, context.Canceled) {
|
||||||
|
return
|
||||||
|
}
|
||||||
bot.logger.Errorln("failed to fetch updates:", err)
|
bot.logger.Errorln("failed to fetch updates:", err)
|
||||||
time.Sleep(2 * time.Second) // exponential backoff
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, u := range updates {
|
for _, update := range updates {
|
||||||
u := u // copy loop variable to avoid race condition
|
u := update // copy loop variable to avoid race condition
|
||||||
select {
|
select {
|
||||||
case bot.updateQueue <- &u:
|
case bot.updateQueue <- &u:
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
@@ -512,6 +535,8 @@ func (bot *Bot[T]) RunWithContext(ctx context.Context) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
pool.Stop() // Wait for all tasks to complete and stop the pool
|
pool.Stop() // Wait for all tasks to complete and stop the pool
|
||||||
|
bot.runnerOnceWG.Wait()
|
||||||
|
bot.runnerBgWG.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run starts the bot using a background context.
|
// Run starts the bot using a background context.
|
||||||
|
|||||||
+26
-6
@@ -85,10 +85,10 @@ func LoadOptsFromEnv() *BotOpts {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
stringUpdateTypes := strings.Split(os.Getenv("UPDATE_TYPES"), ";")
|
stringUpdateTypes := splitEnvList(os.Getenv("UPDATE_TYPES"))
|
||||||
updateTypes := make([]tgapi.UpdateType, len(stringUpdateTypes))
|
updateTypes := make([]tgapi.UpdateType, 0, len(stringUpdateTypes))
|
||||||
for i, updateType := range stringUpdateTypes {
|
for _, updateType := range stringUpdateTypes {
|
||||||
updateTypes[i] = tgapi.UpdateType(updateType)
|
updateTypes = append(updateTypes, tgapi.UpdateType(updateType))
|
||||||
}
|
}
|
||||||
|
|
||||||
return &BotOpts{
|
return &BotOpts{
|
||||||
@@ -119,7 +119,7 @@ func (opts *BotOpts) SetToken(token string) *BotOpts {
|
|||||||
|
|
||||||
// SetUpdateTypes sets the list of update types to listen for.
|
// SetUpdateTypes sets the list of update types to listen for.
|
||||||
// If empty (default), Telegram will return all update types.
|
// If empty (default), Telegram will return all update types.
|
||||||
// Example: opts.SetUpdateTypes("message", "callback_query")
|
// Example: opts.SetUpdateTypes("message", "callback_query").
|
||||||
func (opts *BotOpts) SetUpdateTypes(types ...tgapi.UpdateType) *BotOpts {
|
func (opts *BotOpts) SetUpdateTypes(types ...tgapi.UpdateType) *BotOpts {
|
||||||
opts.UpdateTypes = types
|
opts.UpdateTypes = types
|
||||||
return opts
|
return opts
|
||||||
@@ -222,5 +222,25 @@ func LoadPrefixesFromEnv() []string {
|
|||||||
if !exists {
|
if !exists {
|
||||||
return []string{"/"}
|
return []string{"/"}
|
||||||
}
|
}
|
||||||
return strings.Split(prefixesS, ";")
|
prefixes := splitEnvList(prefixesS)
|
||||||
|
if len(prefixes) == 0 {
|
||||||
|
return []string{"/"}
|
||||||
|
}
|
||||||
|
return prefixes
|
||||||
|
}
|
||||||
|
|
||||||
|
func splitEnvList(value string) []string {
|
||||||
|
if value == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
parts := strings.Split(value, ";")
|
||||||
|
out := make([]string, 0, len(parts))
|
||||||
|
for _, part := range parts {
|
||||||
|
part = strings.TrimSpace(part)
|
||||||
|
if part == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
out = append(out, part)
|
||||||
|
}
|
||||||
|
return out
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package laniakea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"git.nix13.pw/scuroneko/laniakea/tgapi"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestLoadOptsFromEnvIgnoresEmptyUpdateTypes(t *testing.T) {
|
||||||
|
t.Setenv("UPDATE_TYPES", "")
|
||||||
|
|
||||||
|
opts := LoadOptsFromEnv()
|
||||||
|
if len(opts.UpdateTypes) != 0 {
|
||||||
|
t.Fatalf("expected no update types, got %v", opts.UpdateTypes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoadOptsFromEnvSplitsAndTrimsUpdateTypes(t *testing.T) {
|
||||||
|
t.Setenv("UPDATE_TYPES", "message; ; callback_query ")
|
||||||
|
|
||||||
|
opts := LoadOptsFromEnv()
|
||||||
|
want := []tgapi.UpdateType{tgapi.UpdateTypeMessage, tgapi.UpdateTypeCallbackQuery}
|
||||||
|
if !reflect.DeepEqual(opts.UpdateTypes, want) {
|
||||||
|
t.Fatalf("unexpected update types: got %v want %v", opts.UpdateTypes, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoadPrefixesFromEnvDefaultsOnEmptyValue(t *testing.T) {
|
||||||
|
t.Setenv("PREFIXES", "")
|
||||||
|
|
||||||
|
got := LoadPrefixesFromEnv()
|
||||||
|
want := []string{"/"}
|
||||||
|
if !reflect.DeepEqual(got, want) {
|
||||||
|
t.Fatalf("unexpected prefixes: got %v want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoadPrefixesFromEnvDropsEmptyValues(t *testing.T) {
|
||||||
|
t.Setenv("PREFIXES", "/; ; ! ")
|
||||||
|
|
||||||
|
got := LoadPrefixesFromEnv()
|
||||||
|
want := []string{"/", "!"}
|
||||||
|
if !reflect.DeepEqual(got, want) {
|
||||||
|
t.Fatalf("unexpected prefixes: got %v want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
+15
-33
@@ -9,6 +9,7 @@ import (
|
|||||||
"git.nix13.pw/scuroneko/laniakea/tgapi"
|
"git.nix13.pw/scuroneko/laniakea/tgapi"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// CmdRegexp matches command names allowed for Telegram command registration.
|
||||||
var CmdRegexp = regexp.MustCompile("^[a-zA-Z0-9]+$")
|
var CmdRegexp = regexp.MustCompile("^[a-zA-Z0-9]+$")
|
||||||
|
|
||||||
// ErrTooManyCommands is returned when the total number of registered commands
|
// ErrTooManyCommands is returned when the total number of registered commands
|
||||||
@@ -19,21 +20,7 @@ var CmdRegexp = regexp.MustCompile("^[a-zA-Z0-9]+$")
|
|||||||
// bot initialization.
|
// bot initialization.
|
||||||
var ErrTooManyCommands = errors.New("too many commands. max 100")
|
var ErrTooManyCommands = errors.New("too many commands. max 100")
|
||||||
|
|
||||||
// generateBotCommand converts a Command[T] into a tgapi.BotCommand with a
|
// generateBotCommand builds a BotCommand description with generated usage text.
|
||||||
// formatted description that includes usage instructions.
|
|
||||||
//
|
|
||||||
// The description is built as:
|
|
||||||
//
|
|
||||||
// "<original_description>. Usage: /<command> <arg1> [<arg2>] ..."
|
|
||||||
//
|
|
||||||
// Required arguments are shown as-is; optional arguments are wrapped in square brackets.
|
|
||||||
//
|
|
||||||
// Example:
|
|
||||||
//
|
|
||||||
// Command{command: "start", description: "Start the bot", args: []Arg{{text: "name", required: false}}}
|
|
||||||
// → Description: "Start the bot. Usage: /start [name]"
|
|
||||||
// Command{command: "echo", description: "Echo user input", args: []Arg{{text: "name", required: true}}}
|
|
||||||
// → Description: "Echo user input. Usage: /echo <input>"
|
|
||||||
func generateBotCommand[T any](cmd *Command[T]) tgapi.BotCommand {
|
func generateBotCommand[T any](cmd *Command[T]) tgapi.BotCommand {
|
||||||
desc := ""
|
desc := ""
|
||||||
if len(cmd.description) > 0 {
|
if len(cmd.description) > 0 {
|
||||||
@@ -57,16 +44,10 @@ func generateBotCommand[T any](cmd *Command[T]) tgapi.BotCommand {
|
|||||||
return tgapi.BotCommand{Command: cmd.command, Description: usage}
|
return tgapi.BotCommand{Command: cmd.command, Description: usage}
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkCmdRegex check if command satisfy regexp [a-zA-Z0-9]+
|
// checkCmdRegex reports whether cmd matches CmdRegexp.
|
||||||
// Return true if satisfied, else false.
|
|
||||||
func checkCmdRegex(cmd string) bool { return CmdRegexp.MatchString(cmd) }
|
func checkCmdRegex(cmd string) bool { return CmdRegexp.MatchString(cmd) }
|
||||||
|
|
||||||
// gatherCommandsForPlugin collects all non-skipped commands from a Plugin[T]
|
// gatherCommandsForPlugin collects non-skipped, valid commands from one plugin.
|
||||||
// and converts them into tgapi.BotCommand objects.
|
|
||||||
//
|
|
||||||
// Commands marked with skipAutoCmd = true are excluded from auto-registration.
|
|
||||||
// This allows plugins to opt out of automatic command generation (e.g., for
|
|
||||||
// internal or hidden commands).
|
|
||||||
func gatherCommandsForPlugin[T any](pl Plugin[T]) []tgapi.BotCommand {
|
func gatherCommandsForPlugin[T any](pl Plugin[T]) []tgapi.BotCommand {
|
||||||
commands := make([]tgapi.BotCommand, 0)
|
commands := make([]tgapi.BotCommand, 0)
|
||||||
for _, cmd := range pl.commands {
|
for _, cmd := range pl.commands {
|
||||||
@@ -83,7 +64,7 @@ func gatherCommandsForPlugin[T any](pl Plugin[T]) []tgapi.BotCommand {
|
|||||||
|
|
||||||
// gatherCommands collects all commands from all plugins
|
// gatherCommands collects all commands from all plugins
|
||||||
// and converts them into tgapi.BotCommand objects.
|
// and converts them into tgapi.BotCommand objects.
|
||||||
// See gatherCommandsForPlugin
|
// See gatherCommandsForPlugin.
|
||||||
func gatherCommands[T any](bot *Bot[T]) []tgapi.BotCommand {
|
func gatherCommands[T any](bot *Bot[T]) []tgapi.BotCommand {
|
||||||
commands := make([]tgapi.BotCommand, 0)
|
commands := make([]tgapi.BotCommand, 0)
|
||||||
for _, pl := range bot.plugins {
|
for _, pl := range bot.plugins {
|
||||||
@@ -119,17 +100,17 @@ func gatherCommands[T any](bot *Bot[T]) []tgapi.BotCommand {
|
|||||||
// log.Fatal(err)
|
// log.Fatal(err)
|
||||||
// }
|
// }
|
||||||
func (bot *Bot[T]) AutoGenerateCommands() error {
|
func (bot *Bot[T]) AutoGenerateCommands() error {
|
||||||
|
commands := gatherCommands(bot)
|
||||||
|
if len(commands) > 100 {
|
||||||
|
return ErrTooManyCommands
|
||||||
|
}
|
||||||
|
|
||||||
// Clear existing commands to avoid duplication or stale entries
|
// Clear existing commands to avoid duplication or stale entries
|
||||||
_, err := bot.api.DeleteMyCommands(tgapi.DeleteMyCommandsP{})
|
_, err := bot.api.DeleteMyCommands(tgapi.DeleteMyCommandsP{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to delete existing commands: %w", err)
|
return fmt.Errorf("failed to delete existing commands: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
commands := gatherCommands(bot)
|
|
||||||
if len(commands) > 100 {
|
|
||||||
return ErrTooManyCommands
|
|
||||||
}
|
|
||||||
|
|
||||||
// Register commands for each scope
|
// Register commands for each scope
|
||||||
scopes := []*tgapi.BotCommandScope{
|
scopes := []*tgapi.BotCommandScope{
|
||||||
{Type: tgapi.BotCommandScopePrivateType},
|
{Type: tgapi.BotCommandScopePrivateType},
|
||||||
@@ -167,15 +148,16 @@ func (bot *Bot[T]) AutoGenerateCommands() error {
|
|||||||
// log.Fatal(err)
|
// log.Fatal(err)
|
||||||
// }
|
// }
|
||||||
func (bot *Bot[T]) AutoGenerateCommandsForScope(scope *tgapi.BotCommandScope) error {
|
func (bot *Bot[T]) AutoGenerateCommandsForScope(scope *tgapi.BotCommandScope) error {
|
||||||
_, err := bot.api.DeleteMyCommands(tgapi.DeleteMyCommandsP{Scope: scope})
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to delete existing commands: %w", err)
|
|
||||||
}
|
|
||||||
commands := gatherCommands(bot)
|
commands := gatherCommands(bot)
|
||||||
if len(commands) > 100 {
|
if len(commands) > 100 {
|
||||||
return ErrTooManyCommands
|
return ErrTooManyCommands
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_, err := bot.api.DeleteMyCommands(tgapi.DeleteMyCommandsP{Scope: scope})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to delete existing commands: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
_, err = bot.api.SetMyCommands(tgapi.SetMyCommandsP{
|
_, err = bot.api.SetMyCommands(tgapi.SetMyCommandsP{
|
||||||
Commands: commands,
|
Commands: commands,
|
||||||
Scope: scope,
|
Scope: scope,
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
package laniakea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync/atomic"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"git.nix13.pw/scuroneko/laniakea/tgapi"
|
||||||
|
"git.nix13.pw/scuroneko/slog"
|
||||||
|
)
|
||||||
|
|
||||||
|
type roundTripFunc func(*http.Request) (*http.Response, error)
|
||||||
|
|
||||||
|
func (fn roundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||||
|
return fn(req)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAutoGenerateCommandsChecksLimitBeforeDelete(t *testing.T) {
|
||||||
|
var calls atomic.Int64
|
||||||
|
client := &http.Client{
|
||||||
|
Transport: roundTripFunc(func(req *http.Request) (*http.Response, error) {
|
||||||
|
calls.Add(1)
|
||||||
|
return &http.Response{
|
||||||
|
StatusCode: http.StatusOK,
|
||||||
|
Header: http.Header{"Content-Type": []string{"application/json"}},
|
||||||
|
Body: io.NopCloser(strings.NewReader(`{"ok":true,"result":true}`)),
|
||||||
|
}, nil
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
api := tgapi.NewAPI(
|
||||||
|
tgapi.NewAPIOpts("token").
|
||||||
|
SetAPIUrl("https://example.test").
|
||||||
|
SetHTTPClient(client),
|
||||||
|
)
|
||||||
|
defer func() {
|
||||||
|
if err := api.CloseApi(); err != nil {
|
||||||
|
t.Fatalf("CloseApi returned error: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
plugin := NewPlugin[NoDB]("overflow")
|
||||||
|
exec := func(ctx *MsgContext, db *NoDB) {}
|
||||||
|
for i := 0; i < 101; i++ {
|
||||||
|
plugin.AddCommand(NewCommand(exec, "cmd"+strconv.Itoa(i)))
|
||||||
|
}
|
||||||
|
|
||||||
|
bot := &Bot[NoDB]{
|
||||||
|
api: api,
|
||||||
|
logger: slog.CreateLogger(),
|
||||||
|
plugins: []Plugin[NoDB]{*plugin},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := bot.AutoGenerateCommands()
|
||||||
|
if !errors.Is(err, ErrTooManyCommands) {
|
||||||
|
t.Fatalf("expected ErrTooManyCommands, got %v", err)
|
||||||
|
}
|
||||||
|
if calls.Load() != 0 {
|
||||||
|
t.Fatalf("expected no HTTP calls before limit validation, got %d", calls.Load())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"git.nix13.pw/scuroneko/laniakea/tgapi"
|
"git.nix13.pw/scuroneko/laniakea/tgapi"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ErrDraftChatIDZero is returned when a draft is used without setting a chat ID.
|
||||||
var ErrDraftChatIDZero = errors.New("zero draft chat ID")
|
var ErrDraftChatIDZero = errors.New("zero draft chat ID")
|
||||||
|
|
||||||
// draftIdGenerator defines an interface for generating unique draft IDs.
|
// draftIdGenerator defines an interface for generating unique draft IDs.
|
||||||
@@ -90,27 +91,25 @@ func (p *DraftProvider) GetDraft(id uint64) (*Draft, bool) {
|
|||||||
|
|
||||||
// FlushAll sends all pending drafts as final messages and clears them.
|
// FlushAll sends all pending drafts as final messages and clears them.
|
||||||
//
|
//
|
||||||
// If any draft fails to send, FlushAll returns the error immediately and
|
// If one or more drafts fail to send, FlushAll still attempts all drafts and
|
||||||
// leaves other drafts unflushed. This allows for retry logic or logging.
|
// returns the first encountered error.
|
||||||
//
|
//
|
||||||
// After successful flush, each draft is removed from the provider and cleared.
|
// After successful flush, each draft is removed from the provider and cleared.
|
||||||
func (p *DraftProvider) FlushAll() error {
|
func (p *DraftProvider) FlushAll() error {
|
||||||
p.mu.Lock()
|
p.mu.RLock()
|
||||||
drafts := make([]*Draft, 0, len(p.drafts))
|
drafts := make([]*Draft, 0, len(p.drafts))
|
||||||
for _, draft := range p.drafts {
|
for _, draft := range p.drafts {
|
||||||
drafts = append(drafts, draft)
|
drafts = append(drafts, draft)
|
||||||
}
|
}
|
||||||
p.drafts = make(map[uint64]*Draft)
|
p.mu.RUnlock()
|
||||||
p.mu.Unlock()
|
|
||||||
|
|
||||||
var lastErr error
|
var firstErr error
|
||||||
for _, draft := range drafts {
|
for _, draft := range drafts {
|
||||||
if err := draft.Flush(); err != nil {
|
if err := draft.Flush(); err != nil && firstErr == nil {
|
||||||
lastErr = err
|
firstErr = err
|
||||||
break // Stop on first error to avoid partial state
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return lastErr
|
return firstErr
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draft represents a single message draft that can be edited and flushed.
|
// Draft represents a single message draft that can be edited and flushed.
|
||||||
@@ -165,7 +164,7 @@ func (d *Draft) SetChat(chatID int64, messageThreadID int) *Draft {
|
|||||||
// SetEntities replaces the draft's message entities.
|
// SetEntities replaces the draft's message entities.
|
||||||
//
|
//
|
||||||
// Entities are stored by reference. If you plan to mutate the slice later,
|
// Entities are stored by reference. If you plan to mutate the slice later,
|
||||||
// pass a copy: `SetEntities(append([]tgapi.MessageEntity{}, myEntities...))`
|
// pass a copy: `SetEntities(append([]tgapi.MessageEntity{}, myEntities...))`.
|
||||||
func (d *Draft) SetEntities(entities []tgapi.MessageEntity) *Draft {
|
func (d *Draft) SetEntities(entities []tgapi.MessageEntity) *Draft {
|
||||||
d.entities = entities
|
d.entities = entities
|
||||||
return d
|
return d
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ module git.nix13.pw/scuroneko/laniakea
|
|||||||
go 1.26
|
go 1.26
|
||||||
|
|
||||||
require (
|
require (
|
||||||
git.nix13.pw/scuroneko/extypes v1.2.1
|
git.nix13.pw/scuroneko/extypes v1.2.2
|
||||||
git.nix13.pw/scuroneko/slog v1.0.2
|
git.nix13.pw/scuroneko/slog v1.1.2
|
||||||
github.com/alitto/pond/v2 v2.7.0
|
github.com/alitto/pond/v2 v2.7.0
|
||||||
golang.org/x/time v0.15.0
|
golang.org/x/time v0.15.0
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
git.nix13.pw/scuroneko/extypes v1.2.1 h1:IYrOjnWKL2EAuJYtYNa+luB1vBe6paE8VY/YD+5/RpQ=
|
git.nix13.pw/scuroneko/extypes v1.2.2 h1:N54c1ejrPs1yfIkvYuwqI7B1+8S9mDv2GqQA6sct4dk=
|
||||||
git.nix13.pw/scuroneko/extypes v1.2.1/go.mod h1:uZVs8Yo3RrYAG9dMad6qR6lsYY67t+459D9c65QAYAw=
|
git.nix13.pw/scuroneko/extypes v1.2.2/go.mod h1:b4XYk1OW1dVSiE2MT/OMuX/K/UItf1swytX6eroVYnk=
|
||||||
git.nix13.pw/scuroneko/slog v1.0.2 h1:vZyUROygxC2d5FJHUQM/30xFEHY1JT/aweDZXA4rm2g=
|
git.nix13.pw/scuroneko/slog v1.1.2 h1:pl7tV5FN25Yso7sLYoOgBXi9+jLo5BDJHWmHlNPjpY0=
|
||||||
git.nix13.pw/scuroneko/slog v1.0.2/go.mod h1:3Qm2wzkR5KjwOponMfG7TcGSDjmYaFqRAmLvSPTuWJI=
|
git.nix13.pw/scuroneko/slog v1.1.2/go.mod h1:UcfRIHDqpVQHahBGM93awLDK8//AsAvOqBwwbWqMkjM=
|
||||||
github.com/alitto/pond/v2 v2.7.0 h1:c76L+yN916m/DRXjGCeUBHHu92uWnh/g1bwVk4zyyXg=
|
github.com/alitto/pond/v2 v2.7.0 h1:c76L+yN916m/DRXjGCeUBHHu92uWnh/g1bwVk4zyyXg=
|
||||||
github.com/alitto/pond/v2 v2.7.0/go.mod h1:xkjYEgQ05RSpWdfSd1nM3OVv7TBhLdy7rMp3+2Nq+yE=
|
github.com/alitto/pond/v2 v2.7.0/go.mod h1:xkjYEgQ05RSpWdfSd1nM3OVv7TBhLdy7rMp3+2Nq+yE=
|
||||||
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
||||||
|
|||||||
+20
-6
@@ -10,6 +10,7 @@ import (
|
|||||||
"git.nix13.pw/scuroneko/laniakea/tgapi"
|
"git.nix13.pw/scuroneko/laniakea/tgapi"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ErrInvalidPayloadType is returned when callback payload encoding type is unknown.
|
||||||
var ErrInvalidPayloadType = errors.New("invalid payload type")
|
var ErrInvalidPayloadType = errors.New("invalid payload type")
|
||||||
|
|
||||||
func (bot *Bot[T]) handle(u *tgapi.Update) {
|
func (bot *Bot[T]) handle(u *tgapi.Update) {
|
||||||
@@ -28,7 +29,9 @@ func (bot *Bot[T]) handle(u *tgapi.Update) {
|
|||||||
payloadType: bot.payloadType,
|
payloadType: bot.payloadType,
|
||||||
}
|
}
|
||||||
for _, middleware := range bot.middlewares {
|
for _, middleware := range bot.middlewares {
|
||||||
middleware.Execute(ctx, bot.dbContext)
|
if !middleware.Execute(ctx, bot.dbContext) {
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if u.CallbackQuery != nil {
|
if u.CallbackQuery != nil {
|
||||||
@@ -42,6 +45,9 @@ func (bot *Bot[T]) handleMessage(update *tgapi.Update, ctx *MsgContext) {
|
|||||||
if update.Message == nil {
|
if update.Message == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if update.Message.From == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var text string
|
var text string
|
||||||
if len(update.Message.Text) > 0 {
|
if len(update.Message.Text) > 0 {
|
||||||
@@ -106,8 +112,13 @@ func (bot *Bot[T]) handleCallback(update *tgapi.Update, ctx *MsgContext) {
|
|||||||
|
|
||||||
ctx.FromID = update.CallbackQuery.From.ID
|
ctx.FromID = update.CallbackQuery.From.ID
|
||||||
ctx.From = &update.CallbackQuery.From
|
ctx.From = &update.CallbackQuery.From
|
||||||
ctx.Msg = &update.CallbackQuery.Message
|
if update.CallbackQuery.Message != nil {
|
||||||
ctx.CallbackMsgId = update.CallbackQuery.Message.MessageID
|
ctx.Msg = update.CallbackQuery.Message
|
||||||
|
ctx.CallbackMsgId = update.CallbackQuery.Message.MessageID
|
||||||
|
}
|
||||||
|
if update.CallbackQuery.InlineMessageID != nil {
|
||||||
|
ctx.InlineMsgId = *update.CallbackQuery.InlineMessageID
|
||||||
|
}
|
||||||
ctx.CallbackQueryId = update.CallbackQuery.ID
|
ctx.CallbackQueryId = update.CallbackQuery.ID
|
||||||
ctx.Args = data.Args
|
ctx.Args = data.Args
|
||||||
|
|
||||||
@@ -127,6 +138,9 @@ func (bot *Bot[T]) handleCallback(update *tgapi.Update, ctx *MsgContext) {
|
|||||||
|
|
||||||
func (bot *Bot[T]) checkPrefixes(text string) (string, bool) {
|
func (bot *Bot[T]) checkPrefixes(text string) (string, bool) {
|
||||||
for _, prefix := range bot.prefixes {
|
for _, prefix := range bot.prefixes {
|
||||||
|
if prefix == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
if strings.HasPrefix(text, prefix) {
|
if strings.HasPrefix(text, prefix) {
|
||||||
return prefix, true
|
return prefix, true
|
||||||
}
|
}
|
||||||
@@ -151,8 +165,8 @@ func encodeBase64Payload(d CallbackData) (string, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
dst := make([]byte, base64.StdEncoding.EncodedLen(len([]byte(data))))
|
dst := make([]byte, base64.RawURLEncoding.EncodedLen(len([]byte(data))))
|
||||||
base64.StdEncoding.Encode(dst, []byte(data))
|
base64.RawURLEncoding.Encode(dst, []byte(data))
|
||||||
return string(dst), nil
|
return string(dst), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,7 +180,7 @@ func encodeBase64Payload(d CallbackData) (string, error) {
|
|||||||
// return "", ErrInvalidPayloadType
|
// return "", ErrInvalidPayloadType
|
||||||
// }
|
// }
|
||||||
func decodeBase64Payload(s string) (CallbackData, error) {
|
func decodeBase64Payload(s string) (CallbackData, error) {
|
||||||
b, err := base64.StdEncoding.DecodeString(s)
|
b, err := base64.RawURLEncoding.DecodeString(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return CallbackData{}, err
|
return CallbackData{}, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package laniakea
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestCheckPrefixesSkipsEmptyPrefixes(t *testing.T) {
|
||||||
|
bot := &Bot[NoDB]{prefixes: []string{"", "/"}}
|
||||||
|
|
||||||
|
if prefix, ok := bot.checkPrefixes("hello"); ok {
|
||||||
|
t.Fatalf("unexpected prefix match for plain text: %q", prefix)
|
||||||
|
}
|
||||||
|
if prefix, ok := bot.checkPrefixes("/start"); !ok || prefix != "/" {
|
||||||
|
t.Fatalf("unexpected prefix result: prefix=%q ok=%v", prefix, ok)
|
||||||
|
}
|
||||||
|
}
|
||||||
+22
-6
@@ -69,7 +69,7 @@ func (b InlineKbButtonBuilder) SetUrl(url string) InlineKbButtonBuilder {
|
|||||||
// Args are converted to strings using fmt.Sprint. Non-string types (e.g., int, bool)
|
// Args are converted to strings using fmt.Sprint. Non-string types (e.g., int, bool)
|
||||||
// are safely serialized, but complex structs may not serialize usefully.
|
// are safely serialized, but complex structs may not serialize usefully.
|
||||||
//
|
//
|
||||||
// Example: SetCallbackDataJson("delete_user", 123, "confirm") → {"cmd":"delete_user","args":["123","confirm"]}
|
// Example: SetCallbackDataJson("delete_user", 123, "confirm") → {"cmd":"delete_user","args":["123","confirm"]}.
|
||||||
func (b InlineKbButtonBuilder) SetCallbackDataJson(cmd string, args ...any) InlineKbButtonBuilder {
|
func (b InlineKbButtonBuilder) SetCallbackDataJson(cmd string, args ...any) InlineKbButtonBuilder {
|
||||||
b.callbackData = NewCallbackData(cmd, args...).ToJson()
|
b.callbackData = NewCallbackData(cmd, args...).ToJson()
|
||||||
return b
|
return b
|
||||||
@@ -109,16 +109,32 @@ type InlineKeyboard struct {
|
|||||||
payloadType BotPayloadType // Serialization format for callback data (JSON or Base64)
|
payloadType BotPayloadType // Serialization format for callback data (JSON or Base64)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewInlineKeyboard creates a new keyboard builder with the specified maximum
|
// NewInlineKeyboardJson creates a new keyboard builder with the specified maximum
|
||||||
// number of buttons per row.
|
// number of buttons per row.
|
||||||
//
|
//
|
||||||
// Example: NewInlineKeyboard(3) creates a keyboard with at most 3 buttons per line.
|
// Example: NewInlineKeyboardJson(3) creates a keyboard with at most 3 buttons per line.
|
||||||
func NewInlineKeyboard(maxRow int) *InlineKeyboard {
|
func NewInlineKeyboardJson(maxRow int) *InlineKeyboard {
|
||||||
|
return NewInlineKeyboard(BotPayloadJson, maxRow)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewInlineKeyboardBase64 creates a new keyboard builder with the specified maximum
|
||||||
|
// number of buttons per row, using Base64 encoding for button payloads.
|
||||||
|
//
|
||||||
|
// Example: NewInlineKeyboardBase64(3) creates a keyboard with at most 3 buttons per line.
|
||||||
|
func NewInlineKeyboardBase64(maxRow int) *InlineKeyboard {
|
||||||
|
return NewInlineKeyboard(BotPayloadBase64, maxRow)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewInlineKeyboard creates a new keyboard builder with the specified payload encoding
|
||||||
|
// type and maximum number of buttons per row.
|
||||||
|
//
|
||||||
|
// Use NewInlineKeyboardJson or NewInlineKeyboardBase64 for the common cases.
|
||||||
|
func NewInlineKeyboard(payloadType BotPayloadType, maxRow int) *InlineKeyboard {
|
||||||
return &InlineKeyboard{
|
return &InlineKeyboard{
|
||||||
CurrentLine: make(extypes.Slice[tgapi.InlineKeyboardButton], 0),
|
CurrentLine: make(extypes.Slice[tgapi.InlineKeyboardButton], 0),
|
||||||
Lines: make([][]tgapi.InlineKeyboardButton, 0),
|
Lines: make([][]tgapi.InlineKeyboardButton, 0),
|
||||||
maxRow: maxRow,
|
maxRow: maxRow,
|
||||||
payloadType: BotPayloadBase64,
|
payloadType: payloadType,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,7 +210,7 @@ func (in *InlineKeyboard) AddLine() *InlineKeyboard {
|
|||||||
// Returns a pointer to a ReplyMarkup suitable for use with tgapi.SendMessage.
|
// Returns a pointer to a ReplyMarkup suitable for use with tgapi.SendMessage.
|
||||||
func (in *InlineKeyboard) Get() *tgapi.ReplyMarkup {
|
func (in *InlineKeyboard) Get() *tgapi.ReplyMarkup {
|
||||||
if in.CurrentLine.Len() > 0 {
|
if in.CurrentLine.Len() > 0 {
|
||||||
in.Lines = append(in.Lines, in.CurrentLine)
|
in.AddLine()
|
||||||
}
|
}
|
||||||
return &tgapi.ReplyMarkup{InlineKeyboard: in.Lines}
|
return &tgapi.ReplyMarkup{InlineKeyboard: in.Lines}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package laniakea
|
package laniakea
|
||||||
|
|
||||||
// DictEntry represents a single localized entry with language-to-text mappings.
|
// DictEntry represents a single localized entry with language-to-text mappings.
|
||||||
// Example: {"ru": "Привет", "en": "Hello"}
|
// Example: {"ru": "Привет", "en": "Hello"}.
|
||||||
type DictEntry map[string]string
|
type DictEntry map[string]string
|
||||||
|
|
||||||
// L10n is a localization manager that maps keys to language-specific strings.
|
// L10n is a localization manager that maps keys to language-specific strings.
|
||||||
|
|||||||
+8
-6
@@ -1,6 +1,7 @@
|
|||||||
package laniakea
|
package laniakea
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
||||||
"git.nix13.pw/scuroneko/laniakea/tgapi"
|
"git.nix13.pw/scuroneko/laniakea/tgapi"
|
||||||
@@ -12,7 +13,7 @@ import (
|
|||||||
// through AllowedUpdates and includes optional request logging.
|
// through AllowedUpdates and includes optional request logging.
|
||||||
//
|
//
|
||||||
// Parameters:
|
// Parameters:
|
||||||
// - None (uses bot's internal state for offset and allowed updates)
|
// - ctx: request context used to cancel the in-flight long polling request
|
||||||
//
|
//
|
||||||
// Returns:
|
// Returns:
|
||||||
// - []tgapi.Update: slice of received updates (empty if none available)
|
// - []tgapi.Update: slice of received updates (empty if none available)
|
||||||
@@ -26,19 +27,20 @@ import (
|
|||||||
// 5. Automatically updates the offset to the last received update ID + 1
|
// 5. Automatically updates the offset to the last received update ID + 1
|
||||||
// 6. Returns all received updates (empty slice if none)
|
// 6. Returns all received updates (empty slice if none)
|
||||||
//
|
//
|
||||||
// Note: This is a blocking call that waits up to 30 seconds for new updates.
|
// Note: This is a blocking call that waits up to 30 seconds for new updates,
|
||||||
// For non-blocking behavior, consider using webhooks instead.
|
// unless ctx is canceled earlier. For non-blocking behavior, consider using
|
||||||
|
// webhooks instead.
|
||||||
//
|
//
|
||||||
// Example:
|
// Example:
|
||||||
//
|
//
|
||||||
// updates, err := bot.Updates()
|
// updates, err := bot.Updates(ctx)
|
||||||
// if err != nil {
|
// if err != nil {
|
||||||
// log.Fatal(err)
|
// log.Fatal(err)
|
||||||
// }
|
// }
|
||||||
// for _, update := range updates {
|
// for _, update := range updates {
|
||||||
// // process update
|
// // process update
|
||||||
// }
|
// }
|
||||||
func (bot *Bot[T]) Updates() ([]tgapi.Update, error) {
|
func (bot *Bot[T]) Updates(ctx context.Context) ([]tgapi.Update, error) {
|
||||||
offset := bot.GetUpdateOffset()
|
offset := bot.GetUpdateOffset()
|
||||||
params := tgapi.UpdateParams{
|
params := tgapi.UpdateParams{
|
||||||
Offset: Ptr(offset),
|
Offset: Ptr(offset),
|
||||||
@@ -46,7 +48,7 @@ func (bot *Bot[T]) Updates() ([]tgapi.Update, error) {
|
|||||||
AllowedUpdates: bot.GetUpdateTypes(),
|
AllowedUpdates: bot.GetUpdateTypes(),
|
||||||
}
|
}
|
||||||
|
|
||||||
updates, err := bot.api.GetUpdates(params)
|
updates, err := bot.api.GetUpdatesWithContext(ctx, params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
+75
-20
@@ -19,9 +19,10 @@ type MsgContext struct {
|
|||||||
Msg *tgapi.Message
|
Msg *tgapi.Message
|
||||||
From *tgapi.User
|
From *tgapi.User
|
||||||
|
|
||||||
|
InlineMsgId string
|
||||||
CallbackMsgId int
|
CallbackMsgId int
|
||||||
CallbackQueryId string
|
CallbackQueryId string
|
||||||
FromID int
|
FromID int64
|
||||||
Prefix string
|
Prefix string
|
||||||
Text string
|
Text string
|
||||||
Args []string
|
Args []string
|
||||||
@@ -46,11 +47,19 @@ type AnswerMessage struct {
|
|||||||
// Used by Edit, EditMarkdown, EditCallback, etc.
|
// Used by Edit, EditMarkdown, EditCallback, etc.
|
||||||
func (ctx *MsgContext) edit(messageId int, text string, keyboard *InlineKeyboard, parseMode tgapi.ParseMode) *AnswerMessage {
|
func (ctx *MsgContext) edit(messageId int, text string, keyboard *InlineKeyboard, parseMode tgapi.ParseMode) *AnswerMessage {
|
||||||
params := tgapi.EditMessageTextP{
|
params := tgapi.EditMessageTextP{
|
||||||
MessageID: messageId,
|
|
||||||
ChatID: ctx.Msg.Chat.ID,
|
|
||||||
Text: text,
|
Text: text,
|
||||||
ParseMode: parseMode,
|
ParseMode: parseMode,
|
||||||
}
|
}
|
||||||
|
switch {
|
||||||
|
case messageId > 0 && ctx.Msg != nil:
|
||||||
|
params.MessageID = messageId
|
||||||
|
params.ChatID = ctx.Msg.Chat.ID
|
||||||
|
case ctx.InlineMsgId != "":
|
||||||
|
params.InlineMessageID = ctx.InlineMsgId
|
||||||
|
default:
|
||||||
|
ctx.botLogger.Errorln("Can't edit message: no valid message target")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
if keyboard != nil {
|
if keyboard != nil {
|
||||||
params.ReplyMarkup = keyboard.Get()
|
params.ReplyMarkup = keyboard.Get()
|
||||||
}
|
}
|
||||||
@@ -59,8 +68,12 @@ func (ctx *MsgContext) edit(messageId int, text string, keyboard *InlineKeyboard
|
|||||||
ctx.botLogger.Errorln(err)
|
ctx.botLogger.Errorln(err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
resultMessageID := messageId
|
||||||
|
if msg.MessageID > 0 {
|
||||||
|
resultMessageID = msg.MessageID
|
||||||
|
}
|
||||||
return &AnswerMessage{
|
return &AnswerMessage{
|
||||||
MessageID: msg.MessageID, ctx: ctx, Text: text, IsMedia: false,
|
MessageID: resultMessageID, ctx: ctx, Text: text, IsMedia: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,9 +92,9 @@ func (m *AnswerMessage) EditMarkdown(text string) *AnswerMessage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// editCallback is an internal helper to edit the message associated with a callback query.
|
// editCallback is an internal helper to edit the message associated with a callback query.
|
||||||
// Returns nil if CallbackMsgId is 0 (not a callback context).
|
// Supports both regular callback messages and inline callback messages.
|
||||||
func (ctx *MsgContext) editCallback(text string, keyboard *InlineKeyboard, parseMode tgapi.ParseMode) *AnswerMessage {
|
func (ctx *MsgContext) editCallback(text string, keyboard *InlineKeyboard, parseMode tgapi.ParseMode) *AnswerMessage {
|
||||||
if ctx.CallbackMsgId == 0 {
|
if ctx.CallbackMsgId == 0 && ctx.InlineMsgId == "" {
|
||||||
ctx.botLogger.Errorln("Can't edit non-callback update message")
|
ctx.botLogger.Errorln("Can't edit non-callback update message")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -113,18 +126,22 @@ func (ctx *MsgContext) EditCallbackfMarkdown(format string, keyboard *InlineKeyb
|
|||||||
}
|
}
|
||||||
|
|
||||||
// editPhotoText edits the caption of a photo/video message.
|
// editPhotoText edits the caption of a photo/video message.
|
||||||
// Returns nil if messageId is 0.
|
// Returns nil when no valid edit target is available for the current context.
|
||||||
func (ctx *MsgContext) editPhotoText(messageId int, text string, kb *InlineKeyboard, parseMode tgapi.ParseMode) *AnswerMessage {
|
func (ctx *MsgContext) editPhotoText(messageId int, text string, kb *InlineKeyboard, parseMode tgapi.ParseMode) *AnswerMessage {
|
||||||
if messageId == 0 {
|
|
||||||
ctx.botLogger.Errorln("Can't edit caption message, message ID zero")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
params := tgapi.EditMessageCaptionP{
|
params := tgapi.EditMessageCaptionP{
|
||||||
ChatID: ctx.Msg.Chat.ID,
|
|
||||||
MessageID: messageId,
|
|
||||||
Caption: text,
|
Caption: text,
|
||||||
ParseMode: parseMode,
|
ParseMode: parseMode,
|
||||||
}
|
}
|
||||||
|
switch {
|
||||||
|
case messageId > 0 && ctx.Msg != nil:
|
||||||
|
params.ChatID = ctx.Msg.Chat.ID
|
||||||
|
params.MessageID = messageId
|
||||||
|
case ctx.InlineMsgId != "":
|
||||||
|
params.InlineMessageID = ctx.InlineMsgId
|
||||||
|
default:
|
||||||
|
ctx.botLogger.Errorln("Can't edit caption: no valid message target")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
if kb != nil {
|
if kb != nil {
|
||||||
params.ReplyMarkup = kb.Get()
|
params.ReplyMarkup = kb.Get()
|
||||||
}
|
}
|
||||||
@@ -132,9 +149,14 @@ func (ctx *MsgContext) editPhotoText(messageId int, text string, kb *InlineKeybo
|
|||||||
msg, _, err := ctx.Api.EditMessageCaption(params)
|
msg, _, err := ctx.Api.EditMessageCaption(params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.botLogger.Errorln(err)
|
ctx.botLogger.Errorln(err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
resultMessageID := messageId
|
||||||
|
if msg.MessageID > 0 {
|
||||||
|
resultMessageID = msg.MessageID
|
||||||
}
|
}
|
||||||
return &AnswerMessage{
|
return &AnswerMessage{
|
||||||
MessageID: msg.MessageID, ctx: ctx, Text: text, IsMedia: true,
|
MessageID: resultMessageID, ctx: ctx, Text: text, IsMedia: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,6 +187,10 @@ func (m *AnswerMessage) EditCaptionKeyboardMarkdown(text string, kb *InlineKeybo
|
|||||||
// answer sends a new message with optional keyboard and parse mode.
|
// answer sends a new message with optional keyboard and parse mode.
|
||||||
// Uses API limiter to respect Telegram rate limits per chat.
|
// Uses API limiter to respect Telegram rate limits per chat.
|
||||||
func (ctx *MsgContext) answer(text string, keyboard *InlineKeyboard, parseMode tgapi.ParseMode) *AnswerMessage {
|
func (ctx *MsgContext) answer(text string, keyboard *InlineKeyboard, parseMode tgapi.ParseMode) *AnswerMessage {
|
||||||
|
if ctx.Msg == nil {
|
||||||
|
ctx.botLogger.Errorln("Can't answer message without a message")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
params := tgapi.SendMessageP{
|
params := tgapi.SendMessageP{
|
||||||
ChatID: ctx.Msg.Chat.ID,
|
ChatID: ctx.Msg.Chat.ID,
|
||||||
Text: text,
|
Text: text,
|
||||||
@@ -180,11 +206,6 @@ func (ctx *MsgContext) answer(text string, keyboard *InlineKeyboard, parseMode t
|
|||||||
params.DirectMessagesTopicID = ctx.Msg.DirectMessageTopic.TopicID
|
params.DirectMessagesTopicID = ctx.Msg.DirectMessageTopic.TopicID
|
||||||
}
|
}
|
||||||
|
|
||||||
cont := context.Background()
|
|
||||||
if err := ctx.Api.Limiter.Wait(cont, ctx.Msg.Chat.ID); err != nil {
|
|
||||||
ctx.botLogger.Errorln(err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
msg, err := ctx.Api.SendMessage(params)
|
msg, err := ctx.Api.SendMessage(params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.botLogger.Errorln(err)
|
ctx.botLogger.Errorln(err)
|
||||||
@@ -233,6 +254,10 @@ func (ctx *MsgContext) KeyboardMarkdown(text string, keyboard *InlineKeyboard) *
|
|||||||
|
|
||||||
// answerPhoto sends a photo with optional caption and keyboard.
|
// answerPhoto sends a photo with optional caption and keyboard.
|
||||||
func (ctx *MsgContext) answerPhoto(photoId, text string, kb *InlineKeyboard, parseMode tgapi.ParseMode) *AnswerMessage {
|
func (ctx *MsgContext) answerPhoto(photoId, text string, kb *InlineKeyboard, parseMode tgapi.ParseMode) *AnswerMessage {
|
||||||
|
if ctx.Msg == nil {
|
||||||
|
ctx.botLogger.Errorln("Can't answer message without a message")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
params := tgapi.SendPhotoP{
|
params := tgapi.SendPhotoP{
|
||||||
ChatID: ctx.Msg.Chat.ID,
|
ChatID: ctx.Msg.Chat.ID,
|
||||||
Caption: text,
|
Caption: text,
|
||||||
@@ -245,6 +270,9 @@ func (ctx *MsgContext) answerPhoto(photoId, text string, kb *InlineKeyboard, par
|
|||||||
if ctx.Msg.MessageThreadID > 0 {
|
if ctx.Msg.MessageThreadID > 0 {
|
||||||
params.MessageThreadID = ctx.Msg.MessageThreadID
|
params.MessageThreadID = ctx.Msg.MessageThreadID
|
||||||
}
|
}
|
||||||
|
if ctx.Msg.DirectMessageTopic != nil {
|
||||||
|
params.DirectMessagesTopicID = int(ctx.Msg.DirectMessageTopic.TopicID)
|
||||||
|
}
|
||||||
|
|
||||||
msg, err := ctx.Api.SendPhoto(params)
|
msg, err := ctx.Api.SendPhoto(params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -294,6 +322,14 @@ func (ctx *MsgContext) AnswerPhotofMarkdown(photoId, template string, args ...an
|
|||||||
|
|
||||||
// delete removes a message by ID.
|
// delete removes a message by ID.
|
||||||
func (ctx *MsgContext) delete(messageId int) {
|
func (ctx *MsgContext) delete(messageId int) {
|
||||||
|
if messageId == 0 {
|
||||||
|
ctx.botLogger.Errorln("Can't delete message: message ID zero")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ctx.Msg == nil {
|
||||||
|
ctx.botLogger.Errorln("Can't delete message: no chat message context")
|
||||||
|
return
|
||||||
|
}
|
||||||
_, err := ctx.Api.DeleteMessage(tgapi.DeleteMessageP{
|
_, err := ctx.Api.DeleteMessage(tgapi.DeleteMessageP{
|
||||||
ChatID: ctx.Msg.Chat.ID,
|
ChatID: ctx.Msg.Chat.ID,
|
||||||
MessageID: messageId,
|
MessageID: messageId,
|
||||||
@@ -307,7 +343,13 @@ func (ctx *MsgContext) delete(messageId int) {
|
|||||||
func (m *AnswerMessage) Delete() { m.ctx.delete(m.MessageID) }
|
func (m *AnswerMessage) Delete() { m.ctx.delete(m.MessageID) }
|
||||||
|
|
||||||
// CallbackDelete deletes the message that triggered the callback query.
|
// CallbackDelete deletes the message that triggered the callback query.
|
||||||
func (ctx *MsgContext) CallbackDelete() { ctx.delete(ctx.CallbackMsgId) }
|
func (ctx *MsgContext) CallbackDelete() {
|
||||||
|
if ctx.CallbackMsgId == 0 {
|
||||||
|
ctx.botLogger.Errorln("Can't delete callback message: no callback message ID")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.delete(ctx.CallbackMsgId)
|
||||||
|
}
|
||||||
|
|
||||||
// answerCallbackQuery sends a response to a callback query (optional text/alert/url).
|
// answerCallbackQuery sends a response to a callback query (optional text/alert/url).
|
||||||
// Does nothing if CallbackQueryId is empty.
|
// Does nothing if CallbackQueryId is empty.
|
||||||
@@ -338,6 +380,10 @@ func (ctx *MsgContext) AnswerCbQueryUrl(u string) { ctx.answerCallbackQuery(u, "
|
|||||||
|
|
||||||
// SendAction sends a chat action (typing, uploading_photo, etc.) to indicate bot activity.
|
// SendAction sends a chat action (typing, uploading_photo, etc.) to indicate bot activity.
|
||||||
func (ctx *MsgContext) SendAction(action tgapi.ChatActionType) {
|
func (ctx *MsgContext) SendAction(action tgapi.ChatActionType) {
|
||||||
|
if ctx.Msg == nil {
|
||||||
|
ctx.botLogger.Errorln("Can't send action without chat message context")
|
||||||
|
return
|
||||||
|
}
|
||||||
params := tgapi.SendChatActionP{
|
params := tgapi.SendChatActionP{
|
||||||
ChatID: ctx.Msg.Chat.ID, Action: action,
|
ChatID: ctx.Msg.Chat.ID, Action: action,
|
||||||
}
|
}
|
||||||
@@ -391,6 +437,9 @@ func (ctx *MsgContext) NewDraft() *Draft {
|
|||||||
return ctx.newDraft(tgapi.ParseNone)
|
return ctx.newDraft(tgapi.ParseNone)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewDraftMarkdown creates a new message draft associated with the current chat,
|
||||||
|
// with Markdown V2 parse mode enabled.
|
||||||
|
// Uses the API limiter to avoid rate limiting.
|
||||||
func (ctx *MsgContext) NewDraftMarkdown() *Draft {
|
func (ctx *MsgContext) NewDraftMarkdown() *Draft {
|
||||||
return ctx.newDraft(tgapi.ParseMDV2)
|
return ctx.newDraft(tgapi.ParseMDV2)
|
||||||
}
|
}
|
||||||
@@ -404,3 +453,9 @@ func (ctx *MsgContext) Translate(key string) string {
|
|||||||
lang := Val(ctx.From.LanguageCode, ctx.l10n.GetFallbackLanguage())
|
lang := Val(ctx.From.LanguageCode, ctx.l10n.GetFallbackLanguage())
|
||||||
return ctx.l10n.Translate(lang, key)
|
return ctx.l10n.Translate(lang, key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewInlineKeyboard creates a new keyboard builder with the context's payload
|
||||||
|
// encoding type and the specified maximum number of buttons per row.
|
||||||
|
func (ctx *MsgContext) NewInlineKeyboard(maxRow int) *InlineKeyboard {
|
||||||
|
return NewInlineKeyboard(ctx.payloadType, maxRow)
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
package laniakea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"git.nix13.pw/scuroneko/laniakea/tgapi"
|
||||||
|
"git.nix13.pw/scuroneko/slog"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAnswerPhotoIncludesDirectMessagesTopicID(t *testing.T) {
|
||||||
|
var gotBody map[string]any
|
||||||
|
|
||||||
|
client := &http.Client{
|
||||||
|
Transport: roundTripFunc(func(req *http.Request) (*http.Response, error) {
|
||||||
|
body, err := io.ReadAll(req.Body)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to read request body: %v", err)
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(body, &gotBody); err != nil {
|
||||||
|
t.Fatalf("failed to decode request body: %v", err)
|
||||||
|
}
|
||||||
|
return &http.Response{
|
||||||
|
StatusCode: http.StatusOK,
|
||||||
|
Header: http.Header{"Content-Type": []string{"application/json"}},
|
||||||
|
Body: io.NopCloser(strings.NewReader(`{"ok":true,"result":{"message_id":9,"date":1}}`)),
|
||||||
|
}, nil
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
api := tgapi.NewAPI(
|
||||||
|
tgapi.NewAPIOpts("token").
|
||||||
|
SetAPIUrl("https://example.test").
|
||||||
|
SetHTTPClient(client),
|
||||||
|
)
|
||||||
|
defer func() {
|
||||||
|
if err := api.CloseApi(); err != nil {
|
||||||
|
t.Fatalf("CloseApi returned error: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
ctx := &MsgContext{
|
||||||
|
Api: api,
|
||||||
|
Msg: &tgapi.Message{
|
||||||
|
Chat: &tgapi.Chat{ID: 42, Type: string(tgapi.ChatTypePrivate)},
|
||||||
|
DirectMessageTopic: &tgapi.DirectMessageTopic{TopicID: 77},
|
||||||
|
},
|
||||||
|
botLogger: slog.CreateLogger(),
|
||||||
|
}
|
||||||
|
|
||||||
|
answer := ctx.AnswerPhoto("photo-id", "caption")
|
||||||
|
if answer == nil {
|
||||||
|
t.Fatal("expected answer message")
|
||||||
|
}
|
||||||
|
if answer.MessageID != 9 {
|
||||||
|
t.Fatalf("unexpected message id: %d", answer.MessageID)
|
||||||
|
}
|
||||||
|
if got := gotBody["direct_messages_topic_id"]; got != float64(77) {
|
||||||
|
t.Fatalf("unexpected direct_messages_topic_id: %v", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
+7
-15
@@ -23,11 +23,11 @@ const (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
// CommandRegexInt matches one or more digits.
|
// CommandRegexInt matches one or more digits.
|
||||||
CommandRegexInt = regexp.MustCompile(`\d+`)
|
CommandRegexInt = regexp.MustCompile(`^\d+$`)
|
||||||
// CommandRegexString matches any non-empty string.
|
// CommandRegexString matches any non-empty string.
|
||||||
CommandRegexString = regexp.MustCompile(`.+`)
|
CommandRegexString = regexp.MustCompile(`^.+$`)
|
||||||
// CommandRegexBool matches true or false
|
// CommandRegexBool matches true or false.
|
||||||
CommandRegexBool = regexp.MustCompile(`true|false`)
|
CommandRegexBool = regexp.MustCompile(`^(true|false)$`)
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrCmdArgCountMismatch is returned when the number of provided arguments
|
// ErrCmdArgCountMismatch is returned when the number of provided arguments
|
||||||
@@ -53,6 +53,7 @@ func NewCommandArg(text string) *CommandArg {
|
|||||||
return &CommandArg{CommandValueAnyType, text, CommandRegexString, false}
|
return &CommandArg{CommandValueAnyType, text, CommandRegexString, false}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetValueType sets expected value type and switches built-in validation regexp.
|
||||||
func (c *CommandArg) SetValueType(t CommandValueType) *CommandArg {
|
func (c *CommandArg) SetValueType(t CommandValueType) *CommandArg {
|
||||||
regex := CommandRegexString
|
regex := CommandRegexString
|
||||||
switch t {
|
switch t {
|
||||||
@@ -63,6 +64,7 @@ func (c *CommandArg) SetValueType(t CommandValueType) *CommandArg {
|
|||||||
case CommandValueAnyType:
|
case CommandValueAnyType:
|
||||||
regex = nil // Skip validation
|
regex = nil // Skip validation
|
||||||
}
|
}
|
||||||
|
c.valueType = t
|
||||||
c.regex = regex
|
c.regex = regex
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
@@ -96,7 +98,7 @@ func NewCommand[T any](exec CommandExecutor[T], command string, args ...CommandA
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewPayload creates a new Command with the given executor, command payload string, and arguments.
|
// NewPayload creates a new Command with the given executor, command payload string, and arguments.
|
||||||
// The command string can POTENTIALLY contain any symbols, but recommended to use only "_", "-", ".", a-Z, 0-9
|
// The command string can contain any symbols, but it is recommended to use only "_", "-", ".", a-z, A-Z, and 0-9.
|
||||||
func NewPayload[T any](exec CommandExecutor[T], command string, args ...CommandArg) *Command[T] {
|
func NewPayload[T any](exec CommandExecutor[T], command string, args ...CommandArg) *Command[T] {
|
||||||
return &Command[T]{command, "", exec, args, make(extypes.Slice[Middleware[T]], 0), false}
|
return &Command[T]{command, "", exec, args, make(extypes.Slice[Middleware[T]], 0), false}
|
||||||
}
|
}
|
||||||
@@ -223,11 +225,6 @@ func (p *Plugin[T]) executeCmd(cmd string, ctx *MsgContext, dbContext *T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run plugin middlewares
|
|
||||||
if !p.executeMiddlewares(ctx, dbContext) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run command-specific middlewares
|
// Run command-specific middlewares
|
||||||
for _, m := range command.middlewares {
|
for _, m := range command.middlewares {
|
||||||
if !m.Execute(ctx, dbContext) {
|
if !m.Execute(ctx, dbContext) {
|
||||||
@@ -254,11 +251,6 @@ func (p *Plugin[T]) executePayload(payload string, ctx *MsgContext, dbContext *T
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run plugin middlewares
|
|
||||||
if !p.executeMiddlewares(ctx, dbContext) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run command-specific middlewares
|
// Run command-specific middlewares
|
||||||
for _, m := range command.middlewares {
|
for _, m := range command.middlewares {
|
||||||
if !m.Execute(ctx, dbContext) {
|
if !m.Execute(ctx, dbContext) {
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package laniakea
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestValidateArgsRequiresFullMatch(t *testing.T) {
|
||||||
|
intCmd := NewCommand[NoDB](func(ctx *MsgContext, db *NoDB) {}, "int", *NewCommandArg("n").SetValueType(CommandValueIntType).SetRequired())
|
||||||
|
if err := intCmd.validateArgs([]string{"123"}); err != nil {
|
||||||
|
t.Fatalf("expected valid integer argument, got %v", err)
|
||||||
|
}
|
||||||
|
if err := intCmd.validateArgs([]string{"123abc"}); !errors.Is(err, ErrCmdArgRegexpMismatch) {
|
||||||
|
t.Fatalf("expected ErrCmdArgRegexpMismatch for partial int match, got %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
boolCmd := NewCommand[NoDB](func(ctx *MsgContext, db *NoDB) {}, "bool", *NewCommandArg("flag").SetValueType(CommandValueBoolType).SetRequired())
|
||||||
|
if err := boolCmd.validateArgs([]string{"false"}); err != nil {
|
||||||
|
t.Fatalf("expected valid bool argument, got %v", err)
|
||||||
|
}
|
||||||
|
if err := boolCmd.validateArgs([]string{"falsey"}); !errors.Is(err, ErrCmdArgRegexpMismatch) {
|
||||||
|
t.Fatalf("expected ErrCmdArgRegexpMismatch for partial bool match, got %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
+6
-1
@@ -98,12 +98,15 @@ func (bot *Bot[T]) ExecRunners(ctx context.Context) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if !runner.onetime && runner.async && runner.timeout == 0 {
|
if !runner.onetime && runner.async && runner.timeout == 0 {
|
||||||
bot.logger.Warnf("Background runner \"%s\" has no timeout — may cause tight loop\n", runner.name)
|
bot.logger.Warnf("Background runner \"%s\" has no timeout — skipping\n", runner.name)
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if runner.onetime && runner.async {
|
if runner.onetime && runner.async {
|
||||||
// One-time async: fire and forget
|
// One-time async: fire and forget
|
||||||
|
bot.runnerOnceWG.Add(1)
|
||||||
go func(r Runner[T]) {
|
go func(r Runner[T]) {
|
||||||
|
defer bot.runnerOnceWG.Done()
|
||||||
err := r.fn(bot)
|
err := r.fn(bot)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
bot.logger.Warnf("Runner %s failed: %s\n", r.name, err)
|
bot.logger.Warnf("Runner %s failed: %s\n", r.name, err)
|
||||||
@@ -122,7 +125,9 @@ func (bot *Bot[T]) ExecRunners(ctx context.Context) {
|
|||||||
}
|
}
|
||||||
} else if !runner.onetime && runner.async {
|
} else if !runner.onetime && runner.async {
|
||||||
// Background loop: periodic execution with graceful shutdown
|
// Background loop: periodic execution with graceful shutdown
|
||||||
|
bot.runnerBgWG.Add(1)
|
||||||
go func(r Runner[T]) {
|
go func(r Runner[T]) {
|
||||||
|
defer bot.runnerBgWG.Done()
|
||||||
ticker := time.NewTicker(r.timeout)
|
ticker := time.NewTicker(r.timeout)
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
for {
|
for {
|
||||||
|
|||||||
+28
-43
@@ -76,7 +76,11 @@ func (opts *APIOpts) SetLimiterDrop(b bool) *APIOpts {
|
|||||||
return opts
|
return opts
|
||||||
}
|
}
|
||||||
|
|
||||||
// API is the main Telegram Bot API client.
|
// API is the main Telegram Bot API client for JSON requests.
|
||||||
|
//
|
||||||
|
// Use API methods when sending JSON payloads (for example with file_id, URL, or other
|
||||||
|
// non-multipart fields). For multipart file uploads, use Uploader.
|
||||||
|
//
|
||||||
// It manages HTTP requests, rate limiting, retries, and connection pooling.
|
// It manages HTTP requests, rate limiting, retries, and connection pooling.
|
||||||
type API struct {
|
type API struct {
|
||||||
token string
|
token string
|
||||||
@@ -102,7 +106,7 @@ func NewAPI(opts *APIOpts) *API {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pool := newWorkerPool(16, 256)
|
pool := newWorkerPool(16, 256)
|
||||||
pool.start(context.Background())
|
pool.start()
|
||||||
|
|
||||||
return &API{
|
return &API{
|
||||||
token: opts.token,
|
token: opts.token,
|
||||||
@@ -118,12 +122,14 @@ func NewAPI(opts *APIOpts) *API {
|
|||||||
|
|
||||||
// CloseApi shuts down the internal worker pool and closes the logger.
|
// CloseApi shuts down the internal worker pool and closes the logger.
|
||||||
// Must be called to avoid resource leaks.
|
// Must be called to avoid resource leaks.
|
||||||
|
// See https://core.telegram.org/bots/api
|
||||||
func (api *API) CloseApi() error {
|
func (api *API) CloseApi() error {
|
||||||
api.pool.stop()
|
api.pool.stop()
|
||||||
return api.logger.Close()
|
return api.logger.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetLogger returns the internal logger for custom logging.
|
// GetLogger returns the internal logger for custom logging.
|
||||||
|
// See https://core.telegram.org/bots/api
|
||||||
func (api *API) GetLogger() *slog.Logger {
|
func (api *API) GetLogger() *slog.Logger {
|
||||||
return api.logger
|
return api.logger
|
||||||
}
|
}
|
||||||
@@ -161,27 +167,13 @@ type TelegramRequest[R, P any] struct {
|
|||||||
chatId int64
|
chatId int64
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRequest and NewRequestWithChatID are DEPRECATED.
|
// NewRequest creates an untyped TelegramRequest for the given method and params with no chat ID.
|
||||||
// They encourage unsafe, untyped usage and bypass Go's type safety.
|
|
||||||
// Instead, define explicit, type-safe methods for each Telegram API endpoint.
|
|
||||||
//
|
|
||||||
// Example:
|
|
||||||
//
|
|
||||||
// func (api *API) SendMessage(ctx context.Context, chatID int64, text string) (Message, error) { ... }
|
|
||||||
//
|
|
||||||
// This provides:
|
|
||||||
//
|
|
||||||
// ✅ Compile-time validation
|
|
||||||
// ✅ IDE autocompletion
|
|
||||||
// ✅ Clear API surface
|
|
||||||
// ✅ Better error messages
|
|
||||||
//
|
|
||||||
// DO NOT use these constructors in production code.
|
|
||||||
// This can be used ONLY for testing or if you NEED method, that wasn't added as function.
|
|
||||||
func NewRequest[R, P any](method string, params P) TelegramRequest[R, P] {
|
func NewRequest[R, P any](method string, params P) TelegramRequest[R, P] {
|
||||||
return TelegramRequest[R, P]{method, params, 0}
|
return TelegramRequest[R, P]{method, params, 0}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewRequestWithChatID creates an untyped TelegramRequest with an associated chat ID.
|
||||||
|
// The chat ID is used for per-chat rate limiting.
|
||||||
func NewRequestWithChatID[R, P any](method string, params P, chatId int64) TelegramRequest[R, P] {
|
func NewRequestWithChatID[R, P any](method string, params P, chatId int64) TelegramRequest[R, P] {
|
||||||
return TelegramRequest[R, P]{method, params, chatId}
|
return TelegramRequest[R, P]{method, params, chatId}
|
||||||
}
|
}
|
||||||
@@ -191,12 +183,10 @@ func NewRequestWithChatID[R, P any](method string, params P, chatId int64) Teleg
|
|||||||
// Must be called within a worker pool context if using DoWithContext.
|
// Must be called within a worker pool context if using DoWithContext.
|
||||||
func (r TelegramRequest[R, P]) doRequest(ctx context.Context, api *API) (R, error) {
|
func (r TelegramRequest[R, P]) doRequest(ctx context.Context, api *API) (R, error) {
|
||||||
var zero R
|
var zero R
|
||||||
|
reqData, err := json.Marshal(r.params)
|
||||||
data, err := json.Marshal(r.params)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return zero, fmt.Errorf("failed to marshal request: %w", err)
|
return zero, fmt.Errorf("failed to marshal request: %w", err)
|
||||||
}
|
}
|
||||||
buf := bytes.NewBuffer(data)
|
|
||||||
|
|
||||||
methodPrefix := ""
|
methodPrefix := ""
|
||||||
if api.useTestServer {
|
if api.useTestServer {
|
||||||
@@ -204,7 +194,7 @@ func (r TelegramRequest[R, P]) doRequest(ctx context.Context, api *API) (R, erro
|
|||||||
}
|
}
|
||||||
url := fmt.Sprintf("%s/bot%s%s/%s", api.apiUrl, api.token, methodPrefix, r.method)
|
url := fmt.Sprintf("%s/bot%s%s/%s", api.apiUrl, api.token, methodPrefix, r.method)
|
||||||
|
|
||||||
req, err := http.NewRequestWithContext(ctx, "POST", url, buf)
|
req, err := http.NewRequestWithContext(ctx, "POST", url, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return zero, fmt.Errorf("failed to create request: %w", err)
|
return zero, fmt.Errorf("failed to create request: %w", err)
|
||||||
}
|
}
|
||||||
@@ -212,8 +202,6 @@ func (r TelegramRequest[R, P]) doRequest(ctx context.Context, api *API) (R, erro
|
|||||||
req.Header.Set("Content-Type", "application/json")
|
req.Header.Set("Content-Type", "application/json")
|
||||||
req.Header.Set("Accept", "application/json")
|
req.Header.Set("Accept", "application/json")
|
||||||
req.Header.Set("User-Agent", fmt.Sprintf("Laniakea/%s", utils.VersionString))
|
req.Header.Set("User-Agent", fmt.Sprintf("Laniakea/%s", utils.VersionString))
|
||||||
req.Header.Set("Accept-Encoding", "gzip")
|
|
||||||
req.ContentLength = int64(len(data))
|
|
||||||
|
|
||||||
for {
|
for {
|
||||||
// Apply rate limiting before making the request
|
// Apply rate limiting before making the request
|
||||||
@@ -222,22 +210,25 @@ func (r TelegramRequest[R, P]) doRequest(ctx context.Context, api *API) (R, erro
|
|||||||
return zero, err
|
return zero, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
buf := bytes.NewBuffer(reqData)
|
||||||
|
req.Body = io.NopCloser(buf)
|
||||||
|
req.ContentLength = int64(len(reqData))
|
||||||
|
|
||||||
api.logger.Debugln("REQ", url, string(data))
|
api.logger.Debugln("REQ", url, string(reqData))
|
||||||
resp, err := api.client.Do(req)
|
resp, err := api.client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return zero, fmt.Errorf("HTTP request failed: %w", err)
|
return zero, fmt.Errorf("HTTP request failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err = readBody(resp.Body)
|
respData, err := readBody(resp.Body)
|
||||||
_ = resp.Body.Close() // ensure body is closed
|
_ = resp.Body.Close() // ensure body is closed
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return zero, fmt.Errorf("failed to read response body: %w", err)
|
return zero, fmt.Errorf("failed to read response body: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
api.logger.Debugln("RES", r.method, string(data))
|
api.logger.Debugln("RES", r.method, string(respData))
|
||||||
|
|
||||||
response, err := parseBody[R](data)
|
response, err := parseBody[R](respData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return zero, fmt.Errorf("failed to parse response: %w", err)
|
return zero, fmt.Errorf("failed to parse response: %w", err)
|
||||||
}
|
}
|
||||||
@@ -249,10 +240,12 @@ func (r TelegramRequest[R, P]) doRequest(ctx context.Context, api *API) (R, erro
|
|||||||
api.logger.Warnf("Rate limited by Telegram, retry after %d seconds (chat: %d)", after, r.chatId)
|
api.logger.Warnf("Rate limited by Telegram, retry after %d seconds (chat: %d)", after, r.chatId)
|
||||||
|
|
||||||
// Apply cooldown to global or chat-specific limiter
|
// Apply cooldown to global or chat-specific limiter
|
||||||
if r.chatId > 0 {
|
if api.Limiter != nil {
|
||||||
api.Limiter.SetChatLock(r.chatId, after)
|
if r.chatId > 0 {
|
||||||
} else {
|
api.Limiter.SetChatLock(r.chatId, after)
|
||||||
api.Limiter.SetGlobalLock(after)
|
} else {
|
||||||
|
api.Limiter.SetGlobalLock(after)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait and retry
|
// Wait and retry
|
||||||
@@ -311,21 +304,13 @@ func readBody(body io.ReadCloser) ([]byte, error) {
|
|||||||
return io.ReadAll(reader)
|
return io.ReadAll(reader)
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseBody unmarshals Telegram API response and returns structured result.
|
// parseBody unmarshals a Telegram API response into a typed ApiResponse.
|
||||||
// Returns ErrRateLimit internally if error_code == 429 — caller must handle via response.Ok check.
|
// Only returns an error on malformed JSON; non-OK responses are left for the caller to handle.
|
||||||
func parseBody[R any](data []byte) (ApiResponse[R], error) {
|
func parseBody[R any](data []byte) (ApiResponse[R], error) {
|
||||||
var resp ApiResponse[R]
|
var resp ApiResponse[R]
|
||||||
err := json.Unmarshal(data, &resp)
|
err := json.Unmarshal(data, &resp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return resp, fmt.Errorf("failed to unmarshal JSON: %w", err)
|
return resp, fmt.Errorf("failed to unmarshal JSON: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !resp.Ok {
|
|
||||||
if resp.ErrorCode == 429 {
|
|
||||||
return resp, ErrRateLimit // internal use only
|
|
||||||
}
|
|
||||||
return resp, fmt.Errorf("[%d] %s", resp.ErrorCode, resp.Description)
|
|
||||||
}
|
|
||||||
|
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
package tgapi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type roundTripFunc func(*http.Request) (*http.Response, error)
|
||||||
|
|
||||||
|
func (fn roundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||||
|
return fn(req)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAPILeavesAcceptEncodingToHTTPTransport(t *testing.T) {
|
||||||
|
var gotPath string
|
||||||
|
var gotAcceptEncoding string
|
||||||
|
|
||||||
|
client := &http.Client{
|
||||||
|
Transport: roundTripFunc(func(req *http.Request) (*http.Response, error) {
|
||||||
|
gotPath = req.URL.Path
|
||||||
|
gotAcceptEncoding = req.Header.Get("Accept-Encoding")
|
||||||
|
return &http.Response{
|
||||||
|
StatusCode: http.StatusOK,
|
||||||
|
Header: http.Header{"Content-Type": []string{"application/json"}},
|
||||||
|
Body: io.NopCloser(strings.NewReader(`{"ok":true,"result":{"id":1,"is_bot":true,"first_name":"Test"}}`)),
|
||||||
|
}, nil
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
api := NewAPI(
|
||||||
|
NewAPIOpts("token").
|
||||||
|
SetAPIUrl("https://example.test").
|
||||||
|
SetHTTPClient(client),
|
||||||
|
)
|
||||||
|
defer func() {
|
||||||
|
if err := api.CloseApi(); err != nil {
|
||||||
|
t.Fatalf("CloseApi returned error: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
user, err := api.GetMe()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("GetMe returned error: %v", err)
|
||||||
|
}
|
||||||
|
if user.FirstName != "Test" {
|
||||||
|
t.Fatalf("unexpected first name: %q", user.FirstName)
|
||||||
|
}
|
||||||
|
if gotPath != "/bottoken/getMe" {
|
||||||
|
t.Fatalf("unexpected request path: %s", gotPath)
|
||||||
|
}
|
||||||
|
if gotAcceptEncoding != "" {
|
||||||
|
t.Fatalf("expected empty Accept-Encoding header, got %q", gotAcceptEncoding)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
package tgapi
|
package tgapi
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
// SendPhotoP holds parameters for the sendPhoto method.
|
// SendPhotoP holds parameters for the sendPhoto method.
|
||||||
// See https://core.telegram.org/bots/api#sendphoto
|
// See https://core.telegram.org/bots/api#sendphoto
|
||||||
type SendPhotoP struct {
|
type SendPhotoP struct {
|
||||||
@@ -15,7 +17,7 @@ type SendPhotoP struct {
|
|||||||
|
|
||||||
ShowCaptionAboveMedia bool `json:"show_caption_above_media,omitempty"`
|
ShowCaptionAboveMedia bool `json:"show_caption_above_media,omitempty"`
|
||||||
HasSpoiler bool `json:"has_spoiler,omitempty"`
|
HasSpoiler bool `json:"has_spoiler,omitempty"`
|
||||||
DisableNotifications bool `json:"disable_notifications,omitempty"`
|
DisableNotifications bool `json:"disable_notification,omitempty"`
|
||||||
ProtectContent bool `json:"protect_content,omitempty"`
|
ProtectContent bool `json:"protect_content,omitempty"`
|
||||||
AllowPaidBroadcast bool `json:"allow_paid_broadcast,omitempty"`
|
AllowPaidBroadcast bool `json:"allow_paid_broadcast,omitempty"`
|
||||||
MessageEffectID string `json:"message_effect_id,omitempty"`
|
MessageEffectID string `json:"message_effect_id,omitempty"`
|
||||||
@@ -32,6 +34,14 @@ func (api *API) SendPhoto(params SendPhotoP) (Message, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SendPhotoWithContext is the context-aware variant of SendPhoto.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#sendphoto
|
||||||
|
func (api *API) SendPhotoWithContext(ctx context.Context, params SendPhotoP) (Message, error) {
|
||||||
|
req := NewRequestWithChatID[Message]("sendPhoto", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// SendAudioP holds parameters for the sendAudio method.
|
// SendAudioP holds parameters for the sendAudio method.
|
||||||
// See https://core.telegram.org/bots/api#sendaudio
|
// See https://core.telegram.org/bots/api#sendaudio
|
||||||
type SendAudioP struct {
|
type SendAudioP struct {
|
||||||
@@ -47,6 +57,7 @@ type SendAudioP struct {
|
|||||||
Duration int `json:"duration,omitempty"`
|
Duration int `json:"duration,omitempty"`
|
||||||
Performer string `json:"performer,omitempty"`
|
Performer string `json:"performer,omitempty"`
|
||||||
Title string `json:"title,omitempty"`
|
Title string `json:"title,omitempty"`
|
||||||
|
Thumbnail string `json:"thumbnail,omitempty"`
|
||||||
|
|
||||||
DisableNotification bool `json:"disable_notification,omitempty"`
|
DisableNotification bool `json:"disable_notification,omitempty"`
|
||||||
ProtectContent bool `json:"protect_content,omitempty"`
|
ProtectContent bool `json:"protect_content,omitempty"`
|
||||||
@@ -65,6 +76,14 @@ func (api *API) SendAudio(params SendAudioP) (Message, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SendAudioWithContext is the context-aware variant of SendAudio.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#sendaudio
|
||||||
|
func (api *API) SendAudioWithContext(ctx context.Context, params SendAudioP) (Message, error) {
|
||||||
|
req := NewRequestWithChatID[Message]("sendAudio", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// SendDocumentP holds parameters for the sendDocument method.
|
// SendDocumentP holds parameters for the sendDocument method.
|
||||||
// See https://core.telegram.org/bots/api#senddocument
|
// See https://core.telegram.org/bots/api#senddocument
|
||||||
type SendDocumentP struct {
|
type SendDocumentP struct {
|
||||||
@@ -73,10 +92,12 @@ type SendDocumentP struct {
|
|||||||
MessageThreadID int `json:"message_thread_id,omitempty"`
|
MessageThreadID int `json:"message_thread_id,omitempty"`
|
||||||
DirectMessagesTopicID int `json:"direct_messages_topic_id,omitempty"`
|
DirectMessagesTopicID int `json:"direct_messages_topic_id,omitempty"`
|
||||||
|
|
||||||
Document string `json:"document"`
|
Document string `json:"document"`
|
||||||
Caption string `json:"caption,omitempty"`
|
Thumbnail string `json:"thumbnail,omitempty"`
|
||||||
ParseMode ParseMode `json:"parse_mode,omitempty"`
|
Caption string `json:"caption,omitempty"`
|
||||||
CaptionEntities []MessageEntity `json:"caption_entities,omitempty"`
|
ParseMode ParseMode `json:"parse_mode,omitempty"`
|
||||||
|
CaptionEntities []MessageEntity `json:"caption_entities,omitempty"`
|
||||||
|
DisableContentTypeDetection bool `json:"disable_content_type_detection,omitempty"`
|
||||||
|
|
||||||
DisableNotification bool `json:"disable_notification,omitempty"`
|
DisableNotification bool `json:"disable_notification,omitempty"`
|
||||||
ProtectContent bool `json:"protect_content,omitempty"`
|
ProtectContent bool `json:"protect_content,omitempty"`
|
||||||
@@ -95,6 +116,14 @@ func (api *API) SendDocument(params SendDocumentP) (Message, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SendDocumentWithContext is the context-aware variant of SendDocument.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#senddocument
|
||||||
|
func (api *API) SendDocumentWithContext(ctx context.Context, params SendDocumentP) (Message, error) {
|
||||||
|
req := NewRequestWithChatID[Message]("sendDocument", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// SendVideoP holds parameters for the sendVideo method.
|
// SendVideoP holds parameters for the sendVideo method.
|
||||||
// See https://core.telegram.org/bots/api#sendvideo
|
// See https://core.telegram.org/bots/api#sendvideo
|
||||||
type SendVideoP struct {
|
type SendVideoP struct {
|
||||||
@@ -103,11 +132,12 @@ type SendVideoP struct {
|
|||||||
MessageThreadID int `json:"message_thread_id,omitempty"`
|
MessageThreadID int `json:"message_thread_id,omitempty"`
|
||||||
DirectMessagesTopicID int `json:"direct_messages_topic_id,omitempty"`
|
DirectMessagesTopicID int `json:"direct_messages_topic_id,omitempty"`
|
||||||
|
|
||||||
Video string `json:"video"`
|
Video string `json:"video"`
|
||||||
Duration int `json:"duration,omitempty"`
|
Thumbnail string `json:"thumbnail,omitempty"`
|
||||||
Width int `json:"width,omitempty"`
|
Duration int `json:"duration,omitempty"`
|
||||||
Height int `json:"height,omitempty"`
|
Width int `json:"width,omitempty"`
|
||||||
Cover int `json:"cover,omitempty"`
|
Height int `json:"height,omitempty"`
|
||||||
|
Cover string `json:"cover,omitempty"`
|
||||||
|
|
||||||
StartTimestamp int `json:"start_timestamp,omitempty"`
|
StartTimestamp int `json:"start_timestamp,omitempty"`
|
||||||
Caption string `json:"caption,omitempty"`
|
Caption string `json:"caption,omitempty"`
|
||||||
@@ -134,6 +164,14 @@ func (api *API) SendVideo(params SendVideoP) (Message, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SendVideoWithContext is the context-aware variant of SendVideo.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#sendvideo
|
||||||
|
func (api *API) SendVideoWithContext(ctx context.Context, params SendVideoP) (Message, error) {
|
||||||
|
req := NewRequestWithChatID[Message]("sendVideo", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// SendAnimationP holds parameters for the sendAnimation method.
|
// SendAnimationP holds parameters for the sendAnimation method.
|
||||||
// See https://core.telegram.org/bots/api#sendanimation
|
// See https://core.telegram.org/bots/api#sendanimation
|
||||||
type SendAnimationP struct {
|
type SendAnimationP struct {
|
||||||
@@ -143,6 +181,7 @@ type SendAnimationP struct {
|
|||||||
DirectMessagesTopicID int `json:"direct_messages_topic_id,omitempty"`
|
DirectMessagesTopicID int `json:"direct_messages_topic_id,omitempty"`
|
||||||
|
|
||||||
Animation string `json:"animation"`
|
Animation string `json:"animation"`
|
||||||
|
Thumbnail string `json:"thumbnail,omitempty"`
|
||||||
Duration int `json:"duration,omitempty"`
|
Duration int `json:"duration,omitempty"`
|
||||||
Width int `json:"width,omitempty"`
|
Width int `json:"width,omitempty"`
|
||||||
Height int `json:"height,omitempty"`
|
Height int `json:"height,omitempty"`
|
||||||
@@ -169,6 +208,14 @@ func (api *API) SendAnimation(params SendAnimationP) (Message, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SendAnimationWithContext is the context-aware variant of SendAnimation.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#sendanimation
|
||||||
|
func (api *API) SendAnimationWithContext(ctx context.Context, params SendAnimationP) (Message, error) {
|
||||||
|
req := NewRequestWithChatID[Message]("sendAnimation", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// SendVoiceP holds parameters for the sendVoice method.
|
// SendVoiceP holds parameters for the sendVoice method.
|
||||||
// See https://core.telegram.org/bots/api#sendvoice
|
// See https://core.telegram.org/bots/api#sendvoice
|
||||||
type SendVoiceP struct {
|
type SendVoiceP struct {
|
||||||
@@ -199,6 +246,14 @@ func (api *API) SendVoice(params *SendVoiceP) (Message, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SendVoiceWithContext is the context-aware variant of SendVoice.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#sendvoice
|
||||||
|
func (api *API) SendVoiceWithContext(ctx context.Context, params *SendVoiceP) (Message, error) {
|
||||||
|
req := NewRequestWithChatID[Message]("sendVoice", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// SendVideoNoteP holds parameters for the sendVideoNote method.
|
// SendVideoNoteP holds parameters for the sendVideoNote method.
|
||||||
// See https://core.telegram.org/bots/api#sendvideonote
|
// See https://core.telegram.org/bots/api#sendvideonote
|
||||||
type SendVideoNoteP struct {
|
type SendVideoNoteP struct {
|
||||||
@@ -208,6 +263,7 @@ type SendVideoNoteP struct {
|
|||||||
DirectMessagesTopicID int `json:"direct_messages_topic_id,omitempty"`
|
DirectMessagesTopicID int `json:"direct_messages_topic_id,omitempty"`
|
||||||
|
|
||||||
VideoNote string `json:"video_note"`
|
VideoNote string `json:"video_note"`
|
||||||
|
Thumbnail string `json:"thumbnail,omitempty"`
|
||||||
Duration int `json:"duration,omitempty"`
|
Duration int `json:"duration,omitempty"`
|
||||||
Length int `json:"length,omitempty"`
|
Length int `json:"length,omitempty"`
|
||||||
DisableNotification bool `json:"disable_notification,omitempty"`
|
DisableNotification bool `json:"disable_notification,omitempty"`
|
||||||
@@ -227,6 +283,14 @@ func (api *API) SendVideoNote(params SendVideoNoteP) (Message, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SendVideoNoteWithContext is the context-aware variant of SendVideoNote.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#sendvideonote
|
||||||
|
func (api *API) SendVideoNoteWithContext(ctx context.Context, params SendVideoNoteP) (Message, error) {
|
||||||
|
req := NewRequestWithChatID[Message]("sendVideoNote", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// SendPaidMediaP holds parameters for the sendPaidMedia method.
|
// SendPaidMediaP holds parameters for the sendPaidMedia method.
|
||||||
// See https://core.telegram.org/bots/api#sendpaidmedia
|
// See https://core.telegram.org/bots/api#sendpaidmedia
|
||||||
type SendPaidMediaP struct {
|
type SendPaidMediaP struct {
|
||||||
@@ -258,6 +322,14 @@ func (api *API) SendPaidMedia(params SendPaidMediaP) (Message, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SendPaidMediaWithContext is the context-aware variant of SendPaidMedia.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#sendpaidmedia
|
||||||
|
func (api *API) SendPaidMediaWithContext(ctx context.Context, params SendPaidMediaP) (Message, error) {
|
||||||
|
req := NewRequestWithChatID[Message]("sendPaidMedia", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// SendMediaGroupP holds parameters for the sendMediaGroup method.
|
// SendMediaGroupP holds parameters for the sendMediaGroup method.
|
||||||
// See https://core.telegram.org/bots/api#sendmediagroup
|
// See https://core.telegram.org/bots/api#sendmediagroup
|
||||||
type SendMediaGroupP struct {
|
type SendMediaGroupP struct {
|
||||||
@@ -276,7 +348,15 @@ type SendMediaGroupP struct {
|
|||||||
|
|
||||||
// SendMediaGroup sends a group of photos, videos, documents or audios as an album.
|
// SendMediaGroup sends a group of photos, videos, documents or audios as an album.
|
||||||
// See https://core.telegram.org/bots/api#sendmediagroup
|
// See https://core.telegram.org/bots/api#sendmediagroup
|
||||||
func (api *API) SendMediaGroup(params SendMediaGroupP) (Message, error) {
|
func (api *API) SendMediaGroup(params SendMediaGroupP) ([]Message, error) {
|
||||||
req := NewRequestWithChatID[Message]("sendMediaGroup", params, params.ChatID)
|
req := NewRequestWithChatID[[]Message]("sendMediaGroup", params, params.ChatID)
|
||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SendMediaGroupWithContext is the context-aware variant of SendMediaGroup.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#sendmediagroup
|
||||||
|
func (api *API) SendMediaGroupWithContext(ctx context.Context, params SendMediaGroupP) ([]Message, error) {
|
||||||
|
req := NewRequestWithChatID[[]Message]("sendMediaGroup", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|||||||
@@ -70,5 +70,5 @@ type PhotoSize struct {
|
|||||||
FileUniqueID string `json:"file_unique_id"`
|
FileUniqueID string `json:"file_unique_id"`
|
||||||
Width int `json:"width"`
|
Width int `json:"width"`
|
||||||
Height int `json:"height"`
|
Height int `json:"height"`
|
||||||
FileSize int `json:"file_size,omitempty"`
|
FileSize int64 `json:"file_size,omitempty"`
|
||||||
}
|
}
|
||||||
|
|||||||
+151
-5
@@ -1,5 +1,7 @@
|
|||||||
package tgapi
|
package tgapi
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
// SetMyCommandsP holds parameters for the setMyCommands method.
|
// SetMyCommandsP holds parameters for the setMyCommands method.
|
||||||
// See https://core.telegram.org/bots/api#setmycommands
|
// See https://core.telegram.org/bots/api#setmycommands
|
||||||
type SetMyCommandsP struct {
|
type SetMyCommandsP struct {
|
||||||
@@ -16,6 +18,14 @@ func (api *API) SetMyCommands(params SetMyCommandsP) (bool, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetMyCommandsWithContext is the context-aware variant of SetMyCommands.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#setmycommands
|
||||||
|
func (api *API) SetMyCommandsWithContext(ctx context.Context, params SetMyCommandsP) (bool, error) {
|
||||||
|
req := NewRequest[bool]("setMyCommands", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// DeleteMyCommandsP holds parameters for the deleteMyCommands method.
|
// DeleteMyCommandsP holds parameters for the deleteMyCommands method.
|
||||||
// See https://core.telegram.org/bots/api#deletemycommands
|
// See https://core.telegram.org/bots/api#deletemycommands
|
||||||
type DeleteMyCommandsP struct {
|
type DeleteMyCommandsP struct {
|
||||||
@@ -31,6 +41,14 @@ func (api *API) DeleteMyCommands(params DeleteMyCommandsP) (bool, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteMyCommandsWithContext is the context-aware variant of DeleteMyCommands.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#deletemycommands
|
||||||
|
func (api *API) DeleteMyCommandsWithContext(ctx context.Context, params DeleteMyCommandsP) (bool, error) {
|
||||||
|
req := NewRequest[bool]("deleteMyCommands", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// GetMyCommands holds parameters for the getMyCommands method.
|
// GetMyCommands holds parameters for the getMyCommands method.
|
||||||
// See https://core.telegram.org/bots/api#getmycommands
|
// See https://core.telegram.org/bots/api#getmycommands
|
||||||
type GetMyCommands struct {
|
type GetMyCommands struct {
|
||||||
@@ -45,6 +63,14 @@ func (api *API) GetMyCommands(params GetMyCommands) ([]BotCommand, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetMyCommandsWithContext is the context-aware variant of GetMyCommands.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#getmycommands
|
||||||
|
func (api *API) GetMyCommandsWithContext(ctx context.Context, params GetMyCommands) ([]BotCommand, error) {
|
||||||
|
req := NewRequest[[]BotCommand]("getMyCommands", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// SetMyName holds parameters for the setMyName method.
|
// SetMyName holds parameters for the setMyName method.
|
||||||
// See https://core.telegram.org/bots/api#setmyname
|
// See https://core.telegram.org/bots/api#setmyname
|
||||||
type SetMyName struct {
|
type SetMyName struct {
|
||||||
@@ -60,6 +86,14 @@ func (api *API) SetMyName(params SetMyName) (bool, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetMyNameWithContext is the context-aware variant of SetMyName.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#setmyname
|
||||||
|
func (api *API) SetMyNameWithContext(ctx context.Context, params SetMyName) (bool, error) {
|
||||||
|
req := NewRequest[bool]("setMyName", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// GetMyName holds parameters for the getMyName method.
|
// GetMyName holds parameters for the getMyName method.
|
||||||
// See https://core.telegram.org/bots/api#getmyname
|
// See https://core.telegram.org/bots/api#getmyname
|
||||||
type GetMyName struct {
|
type GetMyName struct {
|
||||||
@@ -73,6 +107,14 @@ func (api *API) GetMyName(params GetMyName) (BotName, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetMyNameWithContext is the context-aware variant of GetMyName.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#getmyname
|
||||||
|
func (api *API) GetMyNameWithContext(ctx context.Context, params GetMyName) (BotName, error) {
|
||||||
|
req := NewRequest[BotName]("getMyName", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// SetMyDescription holds parameters for the setMyDescription method.
|
// SetMyDescription holds parameters for the setMyDescription method.
|
||||||
// See https://core.telegram.org/bots/api#setmydescription
|
// See https://core.telegram.org/bots/api#setmydescription
|
||||||
type SetMyDescription struct {
|
type SetMyDescription struct {
|
||||||
@@ -88,6 +130,14 @@ func (api *API) SetMyDescription(params SetMyDescription) (bool, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetMyDescriptionWithContext is the context-aware variant of SetMyDescription.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#setmydescription
|
||||||
|
func (api *API) SetMyDescriptionWithContext(ctx context.Context, params SetMyDescription) (bool, error) {
|
||||||
|
req := NewRequest[bool]("setMyDescription", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// GetMyDescription holds parameters for the getMyDescription method.
|
// GetMyDescription holds parameters for the getMyDescription method.
|
||||||
// See https://core.telegram.org/bots/api#getmydescription
|
// See https://core.telegram.org/bots/api#getmydescription
|
||||||
type GetMyDescription struct {
|
type GetMyDescription struct {
|
||||||
@@ -101,6 +151,14 @@ func (api *API) GetMyDescription(params GetMyDescription) (BotDescription, error
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetMyDescriptionWithContext is the context-aware variant of GetMyDescription.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#getmydescription
|
||||||
|
func (api *API) GetMyDescriptionWithContext(ctx context.Context, params GetMyDescription) (BotDescription, error) {
|
||||||
|
req := NewRequest[BotDescription]("getMyDescription", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// SetMyShortDescription holds parameters for the setMyShortDescription method.
|
// SetMyShortDescription holds parameters for the setMyShortDescription method.
|
||||||
// See https://core.telegram.org/bots/api#setmyshortdescription
|
// See https://core.telegram.org/bots/api#setmyshortdescription
|
||||||
type SetMyShortDescription struct {
|
type SetMyShortDescription struct {
|
||||||
@@ -116,6 +174,14 @@ func (api *API) SetMyShortDescription(params SetMyShortDescription) (bool, error
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetMyShortDescriptionWithContext is the context-aware variant of SetMyShortDescription.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#setmyshortdescription
|
||||||
|
func (api *API) SetMyShortDescriptionWithContext(ctx context.Context, params SetMyShortDescription) (bool, error) {
|
||||||
|
req := NewRequest[bool]("setMyShortDescription", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// GetMyShortDescription holds parameters for the getMyShortDescription method.
|
// GetMyShortDescription holds parameters for the getMyShortDescription method.
|
||||||
// See https://core.telegram.org/bots/api#getmyshortdescription
|
// See https://core.telegram.org/bots/api#getmyshortdescription
|
||||||
type GetMyShortDescription struct {
|
type GetMyShortDescription struct {
|
||||||
@@ -129,6 +195,14 @@ func (api *API) GetMyShortDescription(params GetMyShortDescription) (BotShortDes
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetMyShortDescriptionWithContext is the context-aware variant of GetMyShortDescription.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#getmyshortdescription
|
||||||
|
func (api *API) GetMyShortDescriptionWithContext(ctx context.Context, params GetMyShortDescription) (BotShortDescription, error) {
|
||||||
|
req := NewRequest[BotShortDescription]("getMyShortDescription", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// SetMyProfilePhotoP holds parameters for the setMyProfilePhoto method.
|
// SetMyProfilePhotoP holds parameters for the setMyProfilePhoto method.
|
||||||
// See https://core.telegram.org/bots/api#setmyprofilephoto
|
// See https://core.telegram.org/bots/api#setmyprofilephoto
|
||||||
type SetMyProfilePhotoP struct {
|
type SetMyProfilePhotoP struct {
|
||||||
@@ -143,6 +217,14 @@ func (api *API) SetMyProfilePhoto(params SetMyProfilePhotoP) (bool, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetMyProfilePhotoWithContext is the context-aware variant of SetMyProfilePhoto.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#setmyprofilephoto
|
||||||
|
func (api *API) SetMyProfilePhotoWithContext(ctx context.Context, params SetMyProfilePhotoP) (bool, error) {
|
||||||
|
req := NewRequest[bool]("setMyProfilePhoto", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// RemoveMyProfilePhoto removes the bot's profile photo.
|
// RemoveMyProfilePhoto removes the bot's profile photo.
|
||||||
// Returns true on success.
|
// Returns true on success.
|
||||||
// See https://core.telegram.org/bots/api#removemyprofilephoto
|
// See https://core.telegram.org/bots/api#removemyprofilephoto
|
||||||
@@ -151,10 +233,18 @@ func (api *API) RemoveMyProfilePhoto() (bool, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RemoveMyProfilePhotoWithContext is the context-aware variant of RemoveMyProfilePhoto.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#removemyprofilephoto
|
||||||
|
func (api *API) RemoveMyProfilePhotoWithContext(ctx context.Context) (bool, error) {
|
||||||
|
req := NewRequest[bool]("removeMyProfilePhoto", NoParams)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// SetChatMenuButtonP holds parameters for the setChatMenuButton method.
|
// SetChatMenuButtonP holds parameters for the setChatMenuButton method.
|
||||||
// See https://core.telegram.org/bots/api#setchatmenubutton
|
// See https://core.telegram.org/bots/api#setchatmenubutton
|
||||||
type SetChatMenuButtonP struct {
|
type SetChatMenuButtonP struct {
|
||||||
ChatID int `json:"chat_id"`
|
ChatID int64 `json:"chat_id"`
|
||||||
MenuButton MenuButtonType `json:"menu_button"`
|
MenuButton MenuButtonType `json:"menu_button"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,10 +256,18 @@ func (api *API) SetChatMenuButton(params SetChatMenuButtonP) (bool, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetChatMenuButtonWithContext is the context-aware variant of SetChatMenuButton.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#setchatmenubutton
|
||||||
|
func (api *API) SetChatMenuButtonWithContext(ctx context.Context, params SetChatMenuButtonP) (bool, error) {
|
||||||
|
req := NewRequest[bool]("setChatMenuButton", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// GetChatMenuButtonP holds parameters for the getChatMenuButton method.
|
// GetChatMenuButtonP holds parameters for the getChatMenuButton method.
|
||||||
// See https://core.telegram.org/bots/api#getchatmenubutton
|
// See https://core.telegram.org/bots/api#getchatmenubutton
|
||||||
type GetChatMenuButtonP struct {
|
type GetChatMenuButtonP struct {
|
||||||
ChatID int `json:"chat_id"`
|
ChatID int64 `json:"chat_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetChatMenuButton returns the current menu button for the given chat.
|
// GetChatMenuButton returns the current menu button for the given chat.
|
||||||
@@ -179,6 +277,14 @@ func (api *API) GetChatMenuButton(params GetChatMenuButtonP) (BaseMenuButton, er
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetChatMenuButtonWithContext is the context-aware variant of GetChatMenuButton.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#getchatmenubutton
|
||||||
|
func (api *API) GetChatMenuButtonWithContext(ctx context.Context, params GetChatMenuButtonP) (BaseMenuButton, error) {
|
||||||
|
req := NewRequest[BaseMenuButton]("getChatMenuButton", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// SetMyDefaultAdministratorRightsP holds parameters for the setMyDefaultAdministratorRights method.
|
// SetMyDefaultAdministratorRightsP holds parameters for the setMyDefaultAdministratorRights method.
|
||||||
// See https://core.telegram.org/bots/api#setmydefaultadministratorrights
|
// See https://core.telegram.org/bots/api#setmydefaultadministratorrights
|
||||||
type SetMyDefaultAdministratorRightsP struct {
|
type SetMyDefaultAdministratorRightsP struct {
|
||||||
@@ -194,6 +300,14 @@ func (api *API) SetMyDefaultAdministratorRights(params SetMyDefaultAdministrator
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetMyDefaultAdministratorRightsWithContext is the context-aware variant of SetMyDefaultAdministratorRights.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#setmydefaultadministratorrights
|
||||||
|
func (api *API) SetMyDefaultAdministratorRightsWithContext(ctx context.Context, params SetMyDefaultAdministratorRightsP) (bool, error) {
|
||||||
|
req := NewRequest[bool]("setMyDefaultAdministratorRights", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// GetMyDefaultAdministratorRightsP holds parameters for the getMyDefaultAdministratorRights method.
|
// GetMyDefaultAdministratorRightsP holds parameters for the getMyDefaultAdministratorRights method.
|
||||||
// See https://core.telegram.org/bots/api#getmydefaultadministratorrights
|
// See https://core.telegram.org/bots/api#getmydefaultadministratorrights
|
||||||
type GetMyDefaultAdministratorRightsP struct {
|
type GetMyDefaultAdministratorRightsP struct {
|
||||||
@@ -207,6 +321,14 @@ func (api *API) GetMyDefaultAdministratorRights(params GetMyDefaultAdministrator
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetMyDefaultAdministratorRightsWithContext is the context-aware variant of GetMyDefaultAdministratorRights.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#getmydefaultadministratorrights
|
||||||
|
func (api *API) GetMyDefaultAdministratorRightsWithContext(ctx context.Context, params GetMyDefaultAdministratorRightsP) (ChatAdministratorRights, error) {
|
||||||
|
req := NewRequest[ChatAdministratorRights]("getMyDefaultAdministratorRights", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// GetAvailableGifts returns the list of gifts that can be sent by the bot.
|
// GetAvailableGifts returns the list of gifts that can be sent by the bot.
|
||||||
// See https://core.telegram.org/bots/api#getavailablegifts
|
// See https://core.telegram.org/bots/api#getavailablegifts
|
||||||
func (api *API) GetAvailableGifts() (Gifts, error) {
|
func (api *API) GetAvailableGifts() (Gifts, error) {
|
||||||
@@ -214,11 +336,19 @@ func (api *API) GetAvailableGifts() (Gifts, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetAvailableGiftsWithContext is the context-aware variant of GetAvailableGifts.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#getavailablegifts
|
||||||
|
func (api *API) GetAvailableGiftsWithContext(ctx context.Context) (Gifts, error) {
|
||||||
|
req := NewRequest[Gifts]("getAvailableGifts", NoParams)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// SendGiftP holds parameters for the sendGift method.
|
// SendGiftP holds parameters for the sendGift method.
|
||||||
// See https://core.telegram.org/bots/api#sendgift
|
// See https://core.telegram.org/bots/api#sendgift
|
||||||
type SendGiftP struct {
|
type SendGiftP struct {
|
||||||
UserID int `json:"user_id,omitempty"`
|
UserID int64 `json:"user_id,omitempty"`
|
||||||
ChatID int `json:"chat_id,omitempty"`
|
ChatID int64 `json:"chat_id,omitempty"`
|
||||||
GiftID string `json:"gift_id"`
|
GiftID string `json:"gift_id"`
|
||||||
PayForUpgrade bool `json:"pay_for_upgrade"`
|
PayForUpgrade bool `json:"pay_for_upgrade"`
|
||||||
Text string `json:"text"`
|
Text string `json:"text"`
|
||||||
@@ -234,10 +364,18 @@ func (api *API) SendGift(params SendGiftP) (bool, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SendGiftWithContext is the context-aware variant of SendGift.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#sendgift
|
||||||
|
func (api *API) SendGiftWithContext(ctx context.Context, params SendGiftP) (bool, error) {
|
||||||
|
req := NewRequest[bool]("sendGift", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// GiftPremiumSubscriptionP holds parameters for the giftPremiumSubscription method.
|
// GiftPremiumSubscriptionP holds parameters for the giftPremiumSubscription method.
|
||||||
// See https://core.telegram.org/bots/api#giftpremiumsubscription
|
// See https://core.telegram.org/bots/api#giftpremiumsubscription
|
||||||
type GiftPremiumSubscriptionP struct {
|
type GiftPremiumSubscriptionP struct {
|
||||||
UserID int `json:"user_id"`
|
UserID int64 `json:"user_id"`
|
||||||
MonthCount int `json:"month_count"`
|
MonthCount int `json:"month_count"`
|
||||||
StarCount int `json:"star_count"`
|
StarCount int `json:"star_count"`
|
||||||
Text string `json:"text,omitempty"`
|
Text string `json:"text,omitempty"`
|
||||||
@@ -252,3 +390,11 @@ func (api *API) GiftPremiumSubscription(params GiftPremiumSubscriptionP) (bool,
|
|||||||
req := NewRequest[bool]("giftPremiumSubscription", params)
|
req := NewRequest[bool]("giftPremiumSubscription", params)
|
||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GiftPremiumSubscriptionWithContext is the context-aware variant of GiftPremiumSubscription.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#giftpremiumsubscription
|
||||||
|
func (api *API) GiftPremiumSubscriptionWithContext(ctx context.Context, params GiftPremiumSubscriptionP) (bool, error) {
|
||||||
|
req := NewRequest[bool]("giftPremiumSubscription", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|||||||
+2
-2
@@ -31,8 +31,8 @@ const (
|
|||||||
// See https://core.telegram.org/bots/api#botcommandscope
|
// See https://core.telegram.org/bots/api#botcommandscope
|
||||||
type BotCommandScope struct {
|
type BotCommandScope struct {
|
||||||
Type BotCommandScopeType `json:"type"`
|
Type BotCommandScopeType `json:"type"`
|
||||||
ChatID *int `json:"chat_id,omitempty"`
|
ChatID *int64 `json:"chat_id,omitempty"`
|
||||||
UserID *int `json:"user_id,omitempty"`
|
UserID *int64 `json:"user_id,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// BotName represents the bot's name.
|
// BotName represents the bot's name.
|
||||||
|
|||||||
+229
-22
@@ -1,9 +1,11 @@
|
|||||||
package tgapi
|
package tgapi
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
// VerifyUserP holds parameters for the verifyUser method.
|
// VerifyUserP holds parameters for the verifyUser method.
|
||||||
// See https://core.telegram.org/bots/api#verifyuser
|
// See https://core.telegram.org/bots/api#verifyuser
|
||||||
type VerifyUserP struct {
|
type VerifyUserP struct {
|
||||||
UserID int `json:"user_id"`
|
UserID int64 `json:"user_id"`
|
||||||
CustomDescription string `json:"custom_description,omitempty"`
|
CustomDescription string `json:"custom_description,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -15,10 +17,18 @@ func (api *API) VerifyUser(params VerifyUserP) (bool, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// VerifyUserWithContext is the context-aware variant of VerifyUser.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#verifyuser
|
||||||
|
func (api *API) VerifyUserWithContext(ctx context.Context, params VerifyUserP) (bool, error) {
|
||||||
|
req := NewRequest[bool]("verifyUser", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// VerifyChatP holds parameters for the verifyChat method.
|
// VerifyChatP holds parameters for the verifyChat method.
|
||||||
// See https://core.telegram.org/bots/api#verifychat
|
// See https://core.telegram.org/bots/api#verifychat
|
||||||
type VerifyChatP struct {
|
type VerifyChatP struct {
|
||||||
ChatID int `json:"chat_id"`
|
ChatID int64 `json:"chat_id"`
|
||||||
CustomDescription string `json:"custom_description,omitempty"`
|
CustomDescription string `json:"custom_description,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,10 +40,18 @@ func (api *API) VerifyChat(params VerifyChatP) (bool, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// VerifyChatWithContext is the context-aware variant of VerifyChat.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#verifychat
|
||||||
|
func (api *API) VerifyChatWithContext(ctx context.Context, params VerifyChatP) (bool, error) {
|
||||||
|
req := NewRequest[bool]("verifyChat", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// RemoveUserVerificationP holds parameters for the removeUserVerification method.
|
// RemoveUserVerificationP holds parameters for the removeUserVerification method.
|
||||||
// See https://core.telegram.org/bots/api#removeuserverification
|
// See https://core.telegram.org/bots/api#removeuserverification
|
||||||
type RemoveUserVerificationP struct {
|
type RemoveUserVerificationP struct {
|
||||||
UserID int `json:"user_id"`
|
UserID int64 `json:"user_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveUserVerification removes a user's verification.
|
// RemoveUserVerification removes a user's verification.
|
||||||
@@ -44,10 +62,18 @@ func (api *API) RemoveUserVerification(params RemoveUserVerificationP) (bool, er
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RemoveUserVerificationWithContext is the context-aware variant of RemoveUserVerification.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#removeuserverification
|
||||||
|
func (api *API) RemoveUserVerificationWithContext(ctx context.Context, params RemoveUserVerificationP) (bool, error) {
|
||||||
|
req := NewRequest[bool]("removeUserVerification", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// RemoveChatVerificationP holds parameters for the removeChatVerification method.
|
// RemoveChatVerificationP holds parameters for the removeChatVerification method.
|
||||||
// See https://core.telegram.org/bots/api#removechatverification
|
// See https://core.telegram.org/bots/api#removechatverification
|
||||||
type RemoveChatVerificationP struct {
|
type RemoveChatVerificationP struct {
|
||||||
ChatID int `json:"chat_id"`
|
ChatID int64 `json:"chat_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveChatVerification removes a chat's verification.
|
// RemoveChatVerification removes a chat's verification.
|
||||||
@@ -58,11 +84,19 @@ func (api *API) RemoveChatVerification(params RemoveChatVerificationP) (bool, er
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RemoveChatVerificationWithContext is the context-aware variant of RemoveChatVerification.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#removechatverification
|
||||||
|
func (api *API) RemoveChatVerificationWithContext(ctx context.Context, params RemoveChatVerificationP) (bool, error) {
|
||||||
|
req := NewRequest[bool]("removeChatVerification", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// ReadBusinessMessageP holds parameters for the readBusinessMessage method.
|
// ReadBusinessMessageP holds parameters for the readBusinessMessage method.
|
||||||
// See https://core.telegram.org/bots/api#readbusinessmessage
|
// See https://core.telegram.org/bots/api#readbusinessmessage
|
||||||
type ReadBusinessMessageP struct {
|
type ReadBusinessMessageP struct {
|
||||||
BusinessConnectionID string `json:"business_connection_id"`
|
BusinessConnectionID string `json:"business_connection_id"`
|
||||||
ChatID int `json:"chat_id"`
|
ChatID int64 `json:"chat_id"`
|
||||||
MessageID int `json:"message_id"`
|
MessageID int `json:"message_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,21 +108,58 @@ func (api *API) ReadBusinessMessage(params ReadBusinessMessageP) (bool, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteBusinessMessageP holds parameters for the deleteBusinessMessage method.
|
// ReadBusinessMessageWithContext is the context-aware variant of ReadBusinessMessage.
|
||||||
// See https://core.telegram.org/bots/api#deletebusinessmessage
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
type DeleteBusinessMessageP struct {
|
// See https://core.telegram.org/bots/api#readbusinessmessage
|
||||||
|
func (api *API) ReadBusinessMessageWithContext(ctx context.Context, params ReadBusinessMessageP) (bool, error) {
|
||||||
|
req := NewRequest[bool]("readBusinessMessage", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBusinessConnectionP holds parameters for the getBusinessConnection method.
|
||||||
|
// See https://core.telegram.org/bots/api#getbusinessconnection
|
||||||
|
type GetBusinessConnectionP struct {
|
||||||
|
BusinessConnectionID string `json:"business_connection_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBusinessConnection returns information about a business connection.
|
||||||
|
// See https://core.telegram.org/bots/api#getbusinessconnection
|
||||||
|
func (api *API) GetBusinessConnection(params GetBusinessConnectionP) (BusinessConnection, error) {
|
||||||
|
req := NewRequest[BusinessConnection]("getBusinessConnection", params)
|
||||||
|
return req.Do(api)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBusinessConnectionWithContext is the context-aware variant of GetBusinessConnection.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#getbusinessconnection
|
||||||
|
func (api *API) GetBusinessConnectionWithContext(ctx context.Context, params GetBusinessConnectionP) (BusinessConnection, error) {
|
||||||
|
req := NewRequest[BusinessConnection]("getBusinessConnection", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteBusinessMessagesP holds parameters for the deleteBusinessMessages method.
|
||||||
|
// See https://core.telegram.org/bots/api#deletebusinessmessages
|
||||||
|
type DeleteBusinessMessagesP struct {
|
||||||
BusinessConnectionID string `json:"business_connection_id"`
|
BusinessConnectionID string `json:"business_connection_id"`
|
||||||
MessageIDs []int `json:"message_ids"`
|
MessageIDs []int `json:"message_ids"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteBusinessMessage deletes business messages.
|
// DeleteBusinessMessages deletes business messages.
|
||||||
// Returns true on success.
|
// Returns true on success.
|
||||||
// See https://core.telegram.org/bots/api#deletebusinessmessage
|
// See https://core.telegram.org/bots/api#deletebusinessmessages
|
||||||
func (api *API) DeleteBusinessMessage(params DeleteBusinessMessageP) (bool, error) {
|
func (api *API) DeleteBusinessMessages(params DeleteBusinessMessagesP) (bool, error) {
|
||||||
req := NewRequest[bool]("deleteBusinessMessage", params)
|
req := NewRequest[bool]("deleteBusinessMessages", params)
|
||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteBusinessMessagesWithContext is the context-aware variant of DeleteBusinessMessages.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#deletebusinessmessages
|
||||||
|
func (api *API) DeleteBusinessMessagesWithContext(ctx context.Context, params DeleteBusinessMessagesP) (bool, error) {
|
||||||
|
req := NewRequest[bool]("deleteBusinessMessages", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// SetBusinessAccountNameP holds parameters for the setBusinessAccountName method.
|
// SetBusinessAccountNameP holds parameters for the setBusinessAccountName method.
|
||||||
// See https://core.telegram.org/bots/api#setbusinessaccountname
|
// See https://core.telegram.org/bots/api#setbusinessaccountname
|
||||||
type SetBusinessAccountNameP struct {
|
type SetBusinessAccountNameP struct {
|
||||||
@@ -105,6 +176,14 @@ func (api *API) SetBusinessAccountName(params SetBusinessAccountNameP) (bool, er
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetBusinessAccountNameWithContext is the context-aware variant of SetBusinessAccountName.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#setbusinessaccountname
|
||||||
|
func (api *API) SetBusinessAccountNameWithContext(ctx context.Context, params SetBusinessAccountNameP) (bool, error) {
|
||||||
|
req := NewRequest[bool]("setBusinessAccountName", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// SetBusinessAccountUsernameP holds parameters for the setBusinessAccountUsername method.
|
// SetBusinessAccountUsernameP holds parameters for the setBusinessAccountUsername method.
|
||||||
// See https://core.telegram.org/bots/api#setbusinessaccountusername
|
// See https://core.telegram.org/bots/api#setbusinessaccountusername
|
||||||
type SetBusinessAccountUsernameP struct {
|
type SetBusinessAccountUsernameP struct {
|
||||||
@@ -120,6 +199,14 @@ func (api *API) SetBusinessAccountUsername(params SetBusinessAccountUsernameP) (
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetBusinessAccountUsernameWithContext is the context-aware variant of SetBusinessAccountUsername.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#setbusinessaccountusername
|
||||||
|
func (api *API) SetBusinessAccountUsernameWithContext(ctx context.Context, params SetBusinessAccountUsernameP) (bool, error) {
|
||||||
|
req := NewRequest[bool]("setBusinessAccountUsername", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// SetBusinessAccountBioP holds parameters for the setBusinessAccountBio method.
|
// SetBusinessAccountBioP holds parameters for the setBusinessAccountBio method.
|
||||||
// See https://core.telegram.org/bots/api#setbusinessaccountbio
|
// See https://core.telegram.org/bots/api#setbusinessaccountbio
|
||||||
type SetBusinessAccountBioP struct {
|
type SetBusinessAccountBioP struct {
|
||||||
@@ -135,6 +222,14 @@ func (api *API) SetBusinessAccountBio(params SetBusinessAccountBioP) (bool, erro
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetBusinessAccountBioWithContext is the context-aware variant of SetBusinessAccountBio.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#setbusinessaccountbio
|
||||||
|
func (api *API) SetBusinessAccountBioWithContext(ctx context.Context, params SetBusinessAccountBioP) (bool, error) {
|
||||||
|
req := NewRequest[bool]("setBusinessAccountBio", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// SetBusinessAccountProfilePhoto holds parameters for the setBusinessAccountProfilePhoto method.
|
// SetBusinessAccountProfilePhoto holds parameters for the setBusinessAccountProfilePhoto method.
|
||||||
// See https://core.telegram.org/bots/api#setbusinessaccountprofilephoto
|
// See https://core.telegram.org/bots/api#setbusinessaccountprofilephoto
|
||||||
type SetBusinessAccountProfilePhoto struct {
|
type SetBusinessAccountProfilePhoto struct {
|
||||||
@@ -151,6 +246,14 @@ func (api *API) SetBusinessAccountProfilePhoto(params SetBusinessAccountProfileP
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetBusinessAccountProfilePhotoWithContext is the context-aware variant of SetBusinessAccountProfilePhoto.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#setbusinessaccountprofilephoto
|
||||||
|
func (api *API) SetBusinessAccountProfilePhotoWithContext(ctx context.Context, params SetBusinessAccountProfilePhoto) (bool, error) {
|
||||||
|
req := NewRequest[bool]("setBusinessAccountProfilePhoto", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// RemoveBusinessAccountProfilePhotoP holds parameters for the removeBusinessAccountProfilePhoto method.
|
// RemoveBusinessAccountProfilePhotoP holds parameters for the removeBusinessAccountProfilePhoto method.
|
||||||
// See https://core.telegram.org/bots/api#removebusinessaccountprofilephoto
|
// See https://core.telegram.org/bots/api#removebusinessaccountprofilephoto
|
||||||
type RemoveBusinessAccountProfilePhotoP struct {
|
type RemoveBusinessAccountProfilePhotoP struct {
|
||||||
@@ -166,6 +269,14 @@ func (api *API) RemoveBusinessAccountProfilePhoto(params RemoveBusinessAccountPr
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RemoveBusinessAccountProfilePhotoWithContext is the context-aware variant of RemoveBusinessAccountProfilePhoto.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#removebusinessaccountprofilephoto
|
||||||
|
func (api *API) RemoveBusinessAccountProfilePhotoWithContext(ctx context.Context, params RemoveBusinessAccountProfilePhotoP) (bool, error) {
|
||||||
|
req := NewRequest[bool]("removeBusinessAccountProfilePhoto", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// SetBusinessAccountGiftSettingsP holds parameters for the setBusinessAccountGiftSettings method.
|
// SetBusinessAccountGiftSettingsP holds parameters for the setBusinessAccountGiftSettings method.
|
||||||
// See https://core.telegram.org/bots/api#setbusinessaccountgiftsettings
|
// See https://core.telegram.org/bots/api#setbusinessaccountgiftsettings
|
||||||
type SetBusinessAccountGiftSettingsP struct {
|
type SetBusinessAccountGiftSettingsP struct {
|
||||||
@@ -182,6 +293,14 @@ func (api *API) SetBusinessAccountGiftSettings(params SetBusinessAccountGiftSett
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetBusinessAccountGiftSettingsWithContext is the context-aware variant of SetBusinessAccountGiftSettings.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#setbusinessaccountgiftsettings
|
||||||
|
func (api *API) SetBusinessAccountGiftSettingsWithContext(ctx context.Context, params SetBusinessAccountGiftSettingsP) (bool, error) {
|
||||||
|
req := NewRequest[bool]("setBusinessAccountGiftSettings", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// GetBusinessAccountStarBalanceP holds parameters for the getBusinessAccountStarBalance method.
|
// GetBusinessAccountStarBalanceP holds parameters for the getBusinessAccountStarBalance method.
|
||||||
// See https://core.telegram.org/bots/api#getbusinessaccountstarbalance
|
// See https://core.telegram.org/bots/api#getbusinessaccountstarbalance
|
||||||
type GetBusinessAccountStarBalanceP struct {
|
type GetBusinessAccountStarBalanceP struct {
|
||||||
@@ -191,25 +310,41 @@ type GetBusinessAccountStarBalanceP struct {
|
|||||||
// GetBusinessAccountStarBalance returns the star balance of a business account.
|
// GetBusinessAccountStarBalance returns the star balance of a business account.
|
||||||
// See https://core.telegram.org/bots/api#getbusinessaccountstarbalance
|
// See https://core.telegram.org/bots/api#getbusinessaccountstarbalance
|
||||||
func (api *API) GetBusinessAccountStarBalance(params GetBusinessAccountStarBalanceP) (StarAmount, error) {
|
func (api *API) GetBusinessAccountStarBalance(params GetBusinessAccountStarBalanceP) (StarAmount, error) {
|
||||||
req := NewRequest[StarAmount]("getBusinessAccountGiftSettings", params) // Note: method name in call is incorrect, should be "getBusinessAccountStarBalance". We'll keep as is, but comment refers to correct.
|
req := NewRequest[StarAmount]("getBusinessAccountStarBalance", params)
|
||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TransferBusinessAccountStartP holds parameters for the transferBusinessAccountStart method.
|
// GetBusinessAccountStarBalanceWithContext is the context-aware variant of GetBusinessAccountStarBalance.
|
||||||
// See https://core.telegram.org/bots/api#transferbusinessaccountstart
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
type TransferBusinessAccountStartP struct {
|
// See https://core.telegram.org/bots/api#getbusinessaccountstarbalance
|
||||||
|
func (api *API) GetBusinessAccountStarBalanceWithContext(ctx context.Context, params GetBusinessAccountStarBalanceP) (StarAmount, error) {
|
||||||
|
req := NewRequest[StarAmount]("getBusinessAccountStarBalance", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TransferBusinessAccountStarsP holds parameters for the transferBusinessAccountStars method.
|
||||||
|
// See https://core.telegram.org/bots/api#transferbusinessaccountstars
|
||||||
|
type TransferBusinessAccountStarsP struct {
|
||||||
BusinessConnectionID string `json:"business_connection_id"`
|
BusinessConnectionID string `json:"business_connection_id"`
|
||||||
StarCount int `json:"star_count"`
|
StarCount int `json:"star_count"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TransferBusinessAccountStart transfers stars from a business account.
|
// TransferBusinessAccountStars transfers stars from a business account.
|
||||||
// Returns true on success.
|
// Returns true on success.
|
||||||
// See https://core.telegram.org/bots/api#transferbusinessaccountstart
|
// See https://core.telegram.org/bots/api#transferbusinessaccountstars
|
||||||
func (api *API) TransferBusinessAccountStart(params TransferBusinessAccountStartP) (bool, error) {
|
func (api *API) TransferBusinessAccountStars(params TransferBusinessAccountStarsP) (bool, error) {
|
||||||
req := NewRequest[bool]("transferBusinessAccountStart", params)
|
req := NewRequest[bool]("transferBusinessAccountStars", params)
|
||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TransferBusinessAccountStarsWithContext is the context-aware variant of TransferBusinessAccountStars.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#transferbusinessaccountstars
|
||||||
|
func (api *API) TransferBusinessAccountStarsWithContext(ctx context.Context, params TransferBusinessAccountStarsP) (bool, error) {
|
||||||
|
req := NewRequest[bool]("transferBusinessAccountStars", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// GetBusinessAccountGiftsP holds parameters for the getBusinessAccountGifts method.
|
// GetBusinessAccountGiftsP holds parameters for the getBusinessAccountGifts method.
|
||||||
// See https://core.telegram.org/bots/api#getbusinessaccountgifts
|
// See https://core.telegram.org/bots/api#getbusinessaccountgifts
|
||||||
type GetBusinessAccountGiftsP struct {
|
type GetBusinessAccountGiftsP struct {
|
||||||
@@ -233,6 +368,14 @@ func (api *API) GetBusinessAccountGifts(params GetBusinessAccountGiftsP) (OwnedG
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetBusinessAccountGiftsWithContext is the context-aware variant of GetBusinessAccountGifts.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#getbusinessaccountgifts
|
||||||
|
func (api *API) GetBusinessAccountGiftsWithContext(ctx context.Context, params GetBusinessAccountGiftsP) (OwnedGifts, error) {
|
||||||
|
req := NewRequest[OwnedGifts]("getBusinessAccountGifts", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// ConvertGiftToStarsP holds parameters for the convertGiftToStars method.
|
// ConvertGiftToStarsP holds parameters for the convertGiftToStars method.
|
||||||
// See https://core.telegram.org/bots/api#convertgifttostars
|
// See https://core.telegram.org/bots/api#convertgifttostars
|
||||||
type ConvertGiftToStarsP struct {
|
type ConvertGiftToStarsP struct {
|
||||||
@@ -248,6 +391,14 @@ func (api *API) ConvertGiftToStars(params ConvertGiftToStarsP) (bool, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ConvertGiftToStarsWithContext is the context-aware variant of ConvertGiftToStars.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#convertgifttostars
|
||||||
|
func (api *API) ConvertGiftToStarsWithContext(ctx context.Context, params ConvertGiftToStarsP) (bool, error) {
|
||||||
|
req := NewRequest[bool]("convertGiftToStars", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// UpgradeGiftP holds parameters for the upgradeGift method.
|
// UpgradeGiftP holds parameters for the upgradeGift method.
|
||||||
// See https://core.telegram.org/bots/api#upgradegift
|
// See https://core.telegram.org/bots/api#upgradegift
|
||||||
type UpgradeGiftP struct {
|
type UpgradeGiftP struct {
|
||||||
@@ -265,12 +416,20 @@ func (api *API) UpgradeGift(params UpgradeGiftP) (bool, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpgradeGiftWithContext is the context-aware variant of UpgradeGift.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#upgradegift
|
||||||
|
func (api *API) UpgradeGiftWithContext(ctx context.Context, params UpgradeGiftP) (bool, error) {
|
||||||
|
req := NewRequest[bool]("upgradeGift", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// TransferGiftP holds parameters for the transferGift method.
|
// TransferGiftP holds parameters for the transferGift method.
|
||||||
// See https://core.telegram.org/bots/api#transfergift
|
// See https://core.telegram.org/bots/api#transfergift
|
||||||
type TransferGiftP struct {
|
type TransferGiftP struct {
|
||||||
BusinessConnectionID string `json:"business_connection_id"`
|
BusinessConnectionID string `json:"business_connection_id"`
|
||||||
OwnedGiftID string `json:"owned_gift_id"`
|
OwnedGiftID string `json:"owned_gift_id"`
|
||||||
NewOwnerChatID int `json:"new_owner_chat_id"`
|
NewOwnerChatID int64 `json:"new_owner_chat_id"`
|
||||||
StarCount int `json:"star_count,omitempty"`
|
StarCount int `json:"star_count,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -282,6 +441,14 @@ func (api *API) TransferGift(params TransferGiftP) (bool, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TransferGiftWithContext is the context-aware variant of TransferGift.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#transfergift
|
||||||
|
func (api *API) TransferGiftWithContext(ctx context.Context, params TransferGiftP) (bool, error) {
|
||||||
|
req := NewRequest[bool]("transferGift", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// PostStoryP holds parameters for the postStory method.
|
// PostStoryP holds parameters for the postStory method.
|
||||||
// See https://core.telegram.org/bots/api#poststory
|
// See https://core.telegram.org/bots/api#poststory
|
||||||
type PostStoryP struct {
|
type PostStoryP struct {
|
||||||
@@ -305,6 +472,14 @@ func (api *API) PostStoryPhoto(params PostStoryP) (Story, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PostStoryPhotoWithContext is the context-aware variant of PostStoryPhoto.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#poststory
|
||||||
|
func (api *API) PostStoryPhotoWithContext(ctx context.Context, params PostStoryP) (Story, error) {
|
||||||
|
req := NewRequest[Story]("postStory", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// PostStoryVideo posts a story with a video.
|
// PostStoryVideo posts a story with a video.
|
||||||
// See https://core.telegram.org/bots/api#poststory
|
// See https://core.telegram.org/bots/api#poststory
|
||||||
func (api *API) PostStoryVideo(params PostStoryP) (Story, error) {
|
func (api *API) PostStoryVideo(params PostStoryP) (Story, error) {
|
||||||
@@ -312,11 +487,19 @@ func (api *API) PostStoryVideo(params PostStoryP) (Story, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PostStoryVideoWithContext is the context-aware variant of PostStoryVideo.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#poststory
|
||||||
|
func (api *API) PostStoryVideoWithContext(ctx context.Context, params PostStoryP) (Story, error) {
|
||||||
|
req := NewRequest[Story]("postStory", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// RepostStoryP holds parameters for the repostStory method.
|
// RepostStoryP holds parameters for the repostStory method.
|
||||||
// See https://core.telegram.org/bots/api#repoststory
|
// See https://core.telegram.org/bots/api#repoststory
|
||||||
type RepostStoryP struct {
|
type RepostStoryP struct {
|
||||||
BusinessConnectionID string `json:"business_connection_id"`
|
BusinessConnectionID string `json:"business_connection_id"`
|
||||||
FromChatID int `json:"from_chat_id"`
|
FromChatID int64 `json:"from_chat_id"`
|
||||||
FromStoryID int `json:"from_story_id"`
|
FromStoryID int `json:"from_story_id"`
|
||||||
ActivePeriod int `json:"active_period"`
|
ActivePeriod int `json:"active_period"`
|
||||||
PostToChatPage bool `json:"post_to_chat_page,omitempty"`
|
PostToChatPage bool `json:"post_to_chat_page,omitempty"`
|
||||||
@@ -331,6 +514,14 @@ func (api *API) RepostStory(params RepostStoryP) (Story, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RepostStoryWithContext is the context-aware variant of RepostStory.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#repoststory
|
||||||
|
func (api *API) RepostStoryWithContext(ctx context.Context, params RepostStoryP) (Story, error) {
|
||||||
|
req := NewRequest[Story]("repostStory", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// EditStoryP holds parameters for the editStory method.
|
// EditStoryP holds parameters for the editStory method.
|
||||||
// See https://core.telegram.org/bots/api#editstory
|
// See https://core.telegram.org/bots/api#editstory
|
||||||
type EditStoryP struct {
|
type EditStoryP struct {
|
||||||
@@ -352,6 +543,14 @@ func (api *API) EditStory(params EditStoryP) (Story, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EditStoryWithContext is the context-aware variant of EditStory.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#editstory
|
||||||
|
func (api *API) EditStoryWithContext(ctx context.Context, params EditStoryP) (Story, error) {
|
||||||
|
req := NewRequest[Story]("editStory", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// DeleteStoryP holds parameters for the deleteStory method.
|
// DeleteStoryP holds parameters for the deleteStory method.
|
||||||
// See https://core.telegram.org/bots/api#deletestory
|
// See https://core.telegram.org/bots/api#deletestory
|
||||||
type DeleteStoryP struct {
|
type DeleteStoryP struct {
|
||||||
@@ -366,3 +565,11 @@ func (api *API) DeleteStory(params DeleteStoryP) (bool, error) {
|
|||||||
req := NewRequest[bool]("deleteStory", params)
|
req := NewRequest[bool]("deleteStory", params)
|
||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteStoryWithContext is the context-aware variant of DeleteStory.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#deletestory
|
||||||
|
func (api *API) DeleteStoryWithContext(ctx context.Context, params DeleteStoryP) (bool, error) {
|
||||||
|
req := NewRequest[bool]("deleteStory", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|||||||
@@ -54,12 +54,20 @@ type BusinessBotRights struct {
|
|||||||
type BusinessConnection struct {
|
type BusinessConnection struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
User User `json:"user"`
|
User User `json:"user"`
|
||||||
UserChatID int `json:"user_chat_id"`
|
UserChatID int64 `json:"user_chat_id"`
|
||||||
Date int `json:"date"`
|
Date int `json:"date"`
|
||||||
Rights *BusinessBotRights `json:"rights,omitempty"`
|
Rights *BusinessBotRights `json:"rights,omitempty"`
|
||||||
IsEnabled bool `json:"is_enabled"`
|
IsEnabled bool `json:"is_enabled"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BusinessMessagesDeleted is received when messages are deleted from a connected business account.
|
||||||
|
// See https://core.telegram.org/bots/api#businessmessagesdeleted
|
||||||
|
type BusinessMessagesDeleted struct {
|
||||||
|
BusinessConnectionID string `json:"business_connection_id"`
|
||||||
|
Chat Chat `json:"chat"`
|
||||||
|
MessageIDs []int `json:"message_ids"`
|
||||||
|
}
|
||||||
|
|
||||||
// InputStoryContentType indicates the type of input story content.
|
// InputStoryContentType indicates the type of input story content.
|
||||||
type InputStoryContentType string
|
type InputStoryContentType string
|
||||||
|
|
||||||
|
|||||||
+283
-15
@@ -1,10 +1,12 @@
|
|||||||
package tgapi
|
package tgapi
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
// BanChatMemberP holds parameters for the banChatMember method.
|
// BanChatMemberP holds parameters for the banChatMember method.
|
||||||
// See https://core.telegram.org/bots/api#banchatmember
|
// See https://core.telegram.org/bots/api#banchatmember
|
||||||
type BanChatMemberP struct {
|
type BanChatMemberP struct {
|
||||||
ChatID int64 `json:"chat_id"`
|
ChatID int64 `json:"chat_id"`
|
||||||
UserID int `json:"user_id"`
|
UserID int64 `json:"user_id"`
|
||||||
UntilDate int `json:"until_date,omitempty"`
|
UntilDate int `json:"until_date,omitempty"`
|
||||||
RevokeMessages bool `json:"revoke_messages,omitempty"`
|
RevokeMessages bool `json:"revoke_messages,omitempty"`
|
||||||
}
|
}
|
||||||
@@ -17,11 +19,19 @@ func (api *API) BanChatMember(params BanChatMemberP) (bool, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BanChatMemberWithContext is the context-aware variant of BanChatMember.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#banchatmember
|
||||||
|
func (api *API) BanChatMemberWithContext(ctx context.Context, params BanChatMemberP) (bool, error) {
|
||||||
|
req := NewRequestWithChatID[bool]("banChatMember", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// UnbanChatMemberP holds parameters for the unbanChatMember method.
|
// UnbanChatMemberP holds parameters for the unbanChatMember method.
|
||||||
// See https://core.telegram.org/bots/api#unbanchatmember
|
// See https://core.telegram.org/bots/api#unbanchatmember
|
||||||
type UnbanChatMemberP struct {
|
type UnbanChatMemberP struct {
|
||||||
ChatID int64 `json:"chat_id"`
|
ChatID int64 `json:"chat_id"`
|
||||||
UserID int `json:"user_id"`
|
UserID int64 `json:"user_id"`
|
||||||
OnlyIfBanned bool `json:"only_if_banned"`
|
OnlyIfBanned bool `json:"only_if_banned"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,11 +43,19 @@ func (api *API) UnbanChatMember(params UnbanChatMemberP) (bool, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnbanChatMemberWithContext is the context-aware variant of UnbanChatMember.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#unbanchatmember
|
||||||
|
func (api *API) UnbanChatMemberWithContext(ctx context.Context, params UnbanChatMemberP) (bool, error) {
|
||||||
|
req := NewRequestWithChatID[bool]("unbanChatMember", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// RestrictChatMemberP holds parameters for the restrictChatMember method.
|
// RestrictChatMemberP holds parameters for the restrictChatMember method.
|
||||||
// See https://core.telegram.org/bots/api#restrictchatmember
|
// See https://core.telegram.org/bots/api#restrictchatmember
|
||||||
type RestrictChatMemberP struct {
|
type RestrictChatMemberP struct {
|
||||||
ChatID int64 `json:"chat_id"`
|
ChatID int64 `json:"chat_id"`
|
||||||
UserID int `json:"user_id"`
|
UserID int64 `json:"user_id"`
|
||||||
Permissions ChatPermissions `json:"permissions"`
|
Permissions ChatPermissions `json:"permissions"`
|
||||||
UseIndependentChatPermissions bool `json:"use_independent_chat_permissions,omitempty"`
|
UseIndependentChatPermissions bool `json:"use_independent_chat_permissions,omitempty"`
|
||||||
UntilDate int `json:"until_date,omitempty"`
|
UntilDate int `json:"until_date,omitempty"`
|
||||||
@@ -51,11 +69,19 @@ func (api *API) RestrictChatMember(params RestrictChatMemberP) (bool, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RestrictChatMemberWithContext is the context-aware variant of RestrictChatMember.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#restrictchatmember
|
||||||
|
func (api *API) RestrictChatMemberWithContext(ctx context.Context, params RestrictChatMemberP) (bool, error) {
|
||||||
|
req := NewRequestWithChatID[bool]("restrictChatMember", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// PromoteChatMember holds parameters for the promoteChatMember method.
|
// PromoteChatMember holds parameters for the promoteChatMember method.
|
||||||
// See https://core.telegram.org/bots/api#promotechatmember
|
// See https://core.telegram.org/bots/api#promotechatmember
|
||||||
type PromoteChatMember struct {
|
type PromoteChatMember struct {
|
||||||
ChatID int64 `json:"chat_id"`
|
ChatID int64 `json:"chat_id"`
|
||||||
UserID int `json:"user_id"`
|
UserID int64 `json:"user_id"`
|
||||||
IsAnonymous bool `json:"is_anonymous,omitempty"`
|
IsAnonymous bool `json:"is_anonymous,omitempty"`
|
||||||
|
|
||||||
CanManageChat bool `json:"can_manage_chat,omitempty"`
|
CanManageChat bool `json:"can_manage_chat,omitempty"`
|
||||||
@@ -84,11 +110,19 @@ func (api *API) PromoteChatMember(params PromoteChatMember) (bool, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PromoteChatMemberWithContext is the context-aware variant of PromoteChatMember.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#promotechatmember
|
||||||
|
func (api *API) PromoteChatMemberWithContext(ctx context.Context, params PromoteChatMember) (bool, error) {
|
||||||
|
req := NewRequestWithChatID[bool]("promoteChatMember", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// SetChatAdministratorCustomTitleP holds parameters for the setChatAdministratorCustomTitle method.
|
// SetChatAdministratorCustomTitleP holds parameters for the setChatAdministratorCustomTitle method.
|
||||||
// See https://core.telegram.org/bots/api#setchatadministratorcustomtitle
|
// See https://core.telegram.org/bots/api#setchatadministratorcustomtitle
|
||||||
type SetChatAdministratorCustomTitleP struct {
|
type SetChatAdministratorCustomTitleP struct {
|
||||||
ChatID int64 `json:"chat_id"`
|
ChatID int64 `json:"chat_id"`
|
||||||
UserID int `json:"user_id"`
|
UserID int64 `json:"user_id"`
|
||||||
CustomTitle string `json:"custom_title"`
|
CustomTitle string `json:"custom_title"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,11 +134,19 @@ func (api *API) SetChatAdministratorCustomTitle(params SetChatAdministratorCusto
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetChatAdministratorCustomTitleWithContext is the context-aware variant of SetChatAdministratorCustomTitle.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#setchatadministratorcustomtitle
|
||||||
|
func (api *API) SetChatAdministratorCustomTitleWithContext(ctx context.Context, params SetChatAdministratorCustomTitleP) (bool, error) {
|
||||||
|
req := NewRequestWithChatID[bool]("setChatAdministratorCustomTitle", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// SetChatMemberTagP holds parameters for the setChatMemberTag method.
|
// SetChatMemberTagP holds parameters for the setChatMemberTag method.
|
||||||
// See https://core.telegram.org/bots/api#setchatmembertag
|
// See https://core.telegram.org/bots/api#setchatmembertag
|
||||||
type SetChatMemberTagP struct {
|
type SetChatMemberTagP struct {
|
||||||
ChatID int64 `json:"chat_id"`
|
ChatID int64 `json:"chat_id"`
|
||||||
UserID int `json:"user_id"`
|
UserID int64 `json:"user_id"`
|
||||||
Tag string `json:"tag,omitempty"`
|
Tag string `json:"tag,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,6 +158,14 @@ func (api *API) SetChatMemberTag(params SetChatMemberTagP) (bool, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetChatMemberTagWithContext is the context-aware variant of SetChatMemberTag.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#setchatmembertag
|
||||||
|
func (api *API) SetChatMemberTagWithContext(ctx context.Context, params SetChatMemberTagP) (bool, error) {
|
||||||
|
req := NewRequestWithChatID[bool]("setChatMemberTag", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// BanChatSenderChatP holds parameters for the banChatSenderChat method.
|
// BanChatSenderChatP holds parameters for the banChatSenderChat method.
|
||||||
// See https://core.telegram.org/bots/api#banchatsenderchat
|
// See https://core.telegram.org/bots/api#banchatsenderchat
|
||||||
type BanChatSenderChatP struct {
|
type BanChatSenderChatP struct {
|
||||||
@@ -131,6 +181,14 @@ func (api *API) BanChatSenderChat(params BanChatSenderChatP) (bool, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BanChatSenderChatWithContext is the context-aware variant of BanChatSenderChat.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#banchatsenderchat
|
||||||
|
func (api *API) BanChatSenderChatWithContext(ctx context.Context, params BanChatSenderChatP) (bool, error) {
|
||||||
|
req := NewRequestWithChatID[bool]("banChatSenderChat", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// UnbanChatSenderChatP holds parameters for the unbanChatSenderChat method.
|
// UnbanChatSenderChatP holds parameters for the unbanChatSenderChat method.
|
||||||
// See https://core.telegram.org/bots/api#unbanchatsenderchat
|
// See https://core.telegram.org/bots/api#unbanchatsenderchat
|
||||||
type UnbanChatSenderChatP struct {
|
type UnbanChatSenderChatP struct {
|
||||||
@@ -146,6 +204,14 @@ func (api *API) UnbanChatSenderChat(params UnbanChatSenderChatP) (bool, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnbanChatSenderChatWithContext is the context-aware variant of UnbanChatSenderChat.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#unbanchatsenderchat
|
||||||
|
func (api *API) UnbanChatSenderChatWithContext(ctx context.Context, params UnbanChatSenderChatP) (bool, error) {
|
||||||
|
req := NewRequestWithChatID[bool]("unbanChatSenderChat", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// SetChatPermissionsP holds parameters for the setChatPermissions method.
|
// SetChatPermissionsP holds parameters for the setChatPermissions method.
|
||||||
// See https://core.telegram.org/bots/api#setchatpermissions
|
// See https://core.telegram.org/bots/api#setchatpermissions
|
||||||
type SetChatPermissionsP struct {
|
type SetChatPermissionsP struct {
|
||||||
@@ -162,6 +228,14 @@ func (api *API) SetChatPermissions(params SetChatPermissionsP) (bool, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetChatPermissionsWithContext is the context-aware variant of SetChatPermissions.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#setchatpermissions
|
||||||
|
func (api *API) SetChatPermissionsWithContext(ctx context.Context, params SetChatPermissionsP) (bool, error) {
|
||||||
|
req := NewRequestWithChatID[bool]("setChatPermissions", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// ExportChatInviteLinkP holds parameters for the exportChatInviteLink method.
|
// ExportChatInviteLinkP holds parameters for the exportChatInviteLink method.
|
||||||
// See https://core.telegram.org/bots/api#exportchatinvitelink
|
// See https://core.telegram.org/bots/api#exportchatinvitelink
|
||||||
type ExportChatInviteLinkP struct {
|
type ExportChatInviteLinkP struct {
|
||||||
@@ -176,6 +250,14 @@ func (api *API) ExportChatInviteLink(params ExportChatInviteLinkP) (string, erro
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ExportChatInviteLinkWithContext is the context-aware variant of ExportChatInviteLink.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#exportchatinvitelink
|
||||||
|
func (api *API) ExportChatInviteLinkWithContext(ctx context.Context, params ExportChatInviteLinkP) (string, error) {
|
||||||
|
req := NewRequestWithChatID[string]("exportChatInviteLink", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// CreateChatInviteLinkP holds parameters for the createChatInviteLink method.
|
// CreateChatInviteLinkP holds parameters for the createChatInviteLink method.
|
||||||
// See https://core.telegram.org/bots/api#createchatinvitelink
|
// See https://core.telegram.org/bots/api#createchatinvitelink
|
||||||
type CreateChatInviteLinkP struct {
|
type CreateChatInviteLinkP struct {
|
||||||
@@ -183,7 +265,7 @@ type CreateChatInviteLinkP struct {
|
|||||||
Name *string `json:"name,omitempty"`
|
Name *string `json:"name,omitempty"`
|
||||||
ExpireDate int `json:"expire_date,omitempty"`
|
ExpireDate int `json:"expire_date,omitempty"`
|
||||||
MemberLimit int `json:"member_limit,omitempty"`
|
MemberLimit int `json:"member_limit,omitempty"`
|
||||||
CreatesJoinRequest int `json:"creates_join_request,omitempty"`
|
CreatesJoinRequest bool `json:"creates_join_request,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateChatInviteLink creates an additional invite link for a chat.
|
// CreateChatInviteLink creates an additional invite link for a chat.
|
||||||
@@ -194,6 +276,14 @@ func (api *API) CreateChatInviteLink(params CreateChatInviteLinkP) (ChatInviteLi
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateChatInviteLinkWithContext is the context-aware variant of CreateChatInviteLink.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#createchatinvitelink
|
||||||
|
func (api *API) CreateChatInviteLinkWithContext(ctx context.Context, params CreateChatInviteLinkP) (ChatInviteLink, error) {
|
||||||
|
req := NewRequestWithChatID[ChatInviteLink]("createChatInviteLink", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// EditChatInviteLinkP holds parameters for the editChatInviteLink method.
|
// EditChatInviteLinkP holds parameters for the editChatInviteLink method.
|
||||||
// See https://core.telegram.org/bots/api#editchatinvitelink
|
// See https://core.telegram.org/bots/api#editchatinvitelink
|
||||||
type EditChatInviteLinkP struct {
|
type EditChatInviteLinkP struct {
|
||||||
@@ -203,7 +293,7 @@ type EditChatInviteLinkP struct {
|
|||||||
Name string `json:"name,omitempty"`
|
Name string `json:"name,omitempty"`
|
||||||
ExpireDate int `json:"expire_date,omitempty"`
|
ExpireDate int `json:"expire_date,omitempty"`
|
||||||
MemberLimit int `json:"member_limit,omitempty"`
|
MemberLimit int `json:"member_limit,omitempty"`
|
||||||
CreatesJoinRequest int `json:"creates_join_request,omitempty"`
|
CreatesJoinRequest bool `json:"creates_join_request,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// EditChatInviteLink edits a non‑primary invite link.
|
// EditChatInviteLink edits a non‑primary invite link.
|
||||||
@@ -214,6 +304,14 @@ func (api *API) EditChatInviteLink(params EditChatInviteLinkP) (ChatInviteLink,
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EditChatInviteLinkWithContext is the context-aware variant of EditChatInviteLink.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#editchatinvitelink
|
||||||
|
func (api *API) EditChatInviteLinkWithContext(ctx context.Context, params EditChatInviteLinkP) (ChatInviteLink, error) {
|
||||||
|
req := NewRequestWithChatID[ChatInviteLink]("editChatInviteLink", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// CreateChatSubscriptionInviteLinkP holds parameters for the createChatSubscriptionInviteLink method.
|
// CreateChatSubscriptionInviteLinkP holds parameters for the createChatSubscriptionInviteLink method.
|
||||||
// See https://core.telegram.org/bots/api#createchatsubscriptioninvitelink
|
// See https://core.telegram.org/bots/api#createchatsubscriptioninvitelink
|
||||||
type CreateChatSubscriptionInviteLinkP struct {
|
type CreateChatSubscriptionInviteLinkP struct {
|
||||||
@@ -231,6 +329,14 @@ func (api *API) CreateChatSubscriptionInviteLink(params CreateChatSubscriptionIn
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateChatSubscriptionInviteLinkWithContext is the context-aware variant of CreateChatSubscriptionInviteLink.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#createchatsubscriptioninvitelink
|
||||||
|
func (api *API) CreateChatSubscriptionInviteLinkWithContext(ctx context.Context, params CreateChatSubscriptionInviteLinkP) (ChatInviteLink, error) {
|
||||||
|
req := NewRequestWithChatID[ChatInviteLink]("createChatSubscriptionInviteLink", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// EditChatSubscriptionInviteLinkP holds parameters for the editChatSubscriptionInviteLink method.
|
// EditChatSubscriptionInviteLinkP holds parameters for the editChatSubscriptionInviteLink method.
|
||||||
// See https://core.telegram.org/bots/api#editchatsubscriptioninvitelink
|
// See https://core.telegram.org/bots/api#editchatsubscriptioninvitelink
|
||||||
type EditChatSubscriptionInviteLinkP struct {
|
type EditChatSubscriptionInviteLinkP struct {
|
||||||
@@ -247,6 +353,14 @@ func (api *API) EditChatSubscriptionInviteLink(params EditChatSubscriptionInvite
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EditChatSubscriptionInviteLinkWithContext is the context-aware variant of EditChatSubscriptionInviteLink.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#editchatsubscriptioninvitelink
|
||||||
|
func (api *API) EditChatSubscriptionInviteLinkWithContext(ctx context.Context, params EditChatSubscriptionInviteLinkP) (ChatInviteLink, error) {
|
||||||
|
req := NewRequestWithChatID[ChatInviteLink]("editChatSubscriptionInviteLink", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// RevokeChatInviteLinkP holds parameters for the revokeChatInviteLink method.
|
// RevokeChatInviteLinkP holds parameters for the revokeChatInviteLink method.
|
||||||
// See https://core.telegram.org/bots/api#revokechatinvitelink
|
// See https://core.telegram.org/bots/api#revokechatinvitelink
|
||||||
type RevokeChatInviteLinkP struct {
|
type RevokeChatInviteLinkP struct {
|
||||||
@@ -262,11 +376,19 @@ func (api *API) RevokeChatInviteLink(params RevokeChatInviteLinkP) (ChatInviteLi
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RevokeChatInviteLinkWithContext is the context-aware variant of RevokeChatInviteLink.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#revokechatinvitelink
|
||||||
|
func (api *API) RevokeChatInviteLinkWithContext(ctx context.Context, params RevokeChatInviteLinkP) (ChatInviteLink, error) {
|
||||||
|
req := NewRequestWithChatID[ChatInviteLink]("revokeChatInviteLink", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// ApproveChatJoinRequestP holds parameters for the approveChatJoinRequest method.
|
// ApproveChatJoinRequestP holds parameters for the approveChatJoinRequest method.
|
||||||
// See https://core.telegram.org/bots/api#approvechatjoinrequest
|
// See https://core.telegram.org/bots/api#approvechatjoinrequest
|
||||||
type ApproveChatJoinRequestP struct {
|
type ApproveChatJoinRequestP struct {
|
||||||
ChatID int64 `json:"chat_id"`
|
ChatID int64 `json:"chat_id"`
|
||||||
UserID int `json:"user_id"`
|
UserID int64 `json:"user_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ApproveChatJoinRequest approves a chat join request.
|
// ApproveChatJoinRequest approves a chat join request.
|
||||||
@@ -277,11 +399,19 @@ func (api *API) ApproveChatJoinRequest(params ApproveChatJoinRequestP) (bool, er
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ApproveChatJoinRequestWithContext is the context-aware variant of ApproveChatJoinRequest.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#approvechatjoinrequest
|
||||||
|
func (api *API) ApproveChatJoinRequestWithContext(ctx context.Context, params ApproveChatJoinRequestP) (bool, error) {
|
||||||
|
req := NewRequestWithChatID[bool]("approveChatJoinRequest", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// DeclineChatJoinRequestP holds parameters for the declineChatJoinRequest method.
|
// DeclineChatJoinRequestP holds parameters for the declineChatJoinRequest method.
|
||||||
// See https://core.telegram.org/bots/api#declinechatjoinrequest
|
// See https://core.telegram.org/bots/api#declinechatjoinrequest
|
||||||
type DeclineChatJoinRequestP struct {
|
type DeclineChatJoinRequestP struct {
|
||||||
ChatID int64 `json:"chat_id"`
|
ChatID int64 `json:"chat_id"`
|
||||||
UserID int `json:"user_id"`
|
UserID int64 `json:"user_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeclineChatJoinRequest declines a chat join request.
|
// DeclineChatJoinRequest declines a chat join request.
|
||||||
@@ -292,13 +422,31 @@ func (api *API) DeclineChatJoinRequest(params DeclineChatJoinRequestP) (bool, er
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetChatPhoto is a stub method (needs implementation).
|
// DeclineChatJoinRequestWithContext is the context-aware variant of DeclineChatJoinRequest.
|
||||||
// Currently incomplete.
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
func (api *API) SetChatPhoto() {
|
// See https://core.telegram.org/bots/api#declinechatjoinrequest
|
||||||
|
func (api *API) DeclineChatJoinRequestWithContext(ctx context.Context, params DeclineChatJoinRequestP) (bool, error) {
|
||||||
|
req := NewRequestWithChatID[bool]("declineChatJoinRequest", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetChatPhotoP holds parameters for the setChatPhoto method.
|
||||||
|
// See https://core.telegram.org/bots/api#setchatphoto
|
||||||
|
type SetChatPhotoP struct {
|
||||||
|
ChatID int64 `json:"chat_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetChatPhoto changes the chat photo.
|
||||||
|
// photo is the file to upload as the new photo.
|
||||||
|
// Returns True on success.
|
||||||
|
// See https://core.telegram.org/bots/api#setchatphoto
|
||||||
|
func (api *API) SetChatPhoto(params SetChatPhotoP, photo UploaderFile) (bool, error) {
|
||||||
uploader := NewUploader(api)
|
uploader := NewUploader(api)
|
||||||
defer func() {
|
defer func() {
|
||||||
_ = uploader.Close()
|
_ = uploader.Close()
|
||||||
}()
|
}()
|
||||||
|
req := NewUploaderRequestWithChatID[bool]("setChatPhoto", params, params.ChatID, photo.SetType(UploaderPhotoType))
|
||||||
|
return req.Do(uploader)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteChatPhotoP holds parameters for the deleteChatPhoto method.
|
// DeleteChatPhotoP holds parameters for the deleteChatPhoto method.
|
||||||
@@ -315,6 +463,14 @@ func (api *API) DeleteChatPhoto(params DeleteChatPhotoP) (bool, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteChatPhotoWithContext is the context-aware variant of DeleteChatPhoto.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#deletechatphoto
|
||||||
|
func (api *API) DeleteChatPhotoWithContext(ctx context.Context, params DeleteChatPhotoP) (bool, error) {
|
||||||
|
req := NewRequestWithChatID[bool]("deleteChatPhoto", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// SetChatTitleP holds parameters for the setChatTitle method.
|
// SetChatTitleP holds parameters for the setChatTitle method.
|
||||||
// See https://core.telegram.org/bots/api#setchattitle
|
// See https://core.telegram.org/bots/api#setchattitle
|
||||||
type SetChatTitleP struct {
|
type SetChatTitleP struct {
|
||||||
@@ -330,6 +486,14 @@ func (api *API) SetChatTitle(params SetChatTitleP) (bool, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetChatTitleWithContext is the context-aware variant of SetChatTitle.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#setchattitle
|
||||||
|
func (api *API) SetChatTitleWithContext(ctx context.Context, params SetChatTitleP) (bool, error) {
|
||||||
|
req := NewRequestWithChatID[bool]("setChatTitle", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// SetChatDescriptionP holds parameters for the setChatDescription method.
|
// SetChatDescriptionP holds parameters for the setChatDescription method.
|
||||||
// See https://core.telegram.org/bots/api#setchatdescription
|
// See https://core.telegram.org/bots/api#setchatdescription
|
||||||
type SetChatDescriptionP struct {
|
type SetChatDescriptionP struct {
|
||||||
@@ -345,6 +509,14 @@ func (api *API) SetChatDescription(params SetChatDescriptionP) (bool, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetChatDescriptionWithContext is the context-aware variant of SetChatDescription.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#setchatdescription
|
||||||
|
func (api *API) SetChatDescriptionWithContext(ctx context.Context, params SetChatDescriptionP) (bool, error) {
|
||||||
|
req := NewRequestWithChatID[bool]("setChatDescription", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// PinChatMessageP holds parameters for the pinChatMessage method.
|
// PinChatMessageP holds parameters for the pinChatMessage method.
|
||||||
// See https://core.telegram.org/bots/api#pinchatmessage
|
// See https://core.telegram.org/bots/api#pinchatmessage
|
||||||
type PinChatMessageP struct {
|
type PinChatMessageP struct {
|
||||||
@@ -362,6 +534,14 @@ func (api *API) PinChatMessage(params PinChatMessageP) (bool, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PinChatMessageWithContext is the context-aware variant of PinChatMessage.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#pinchatmessage
|
||||||
|
func (api *API) PinChatMessageWithContext(ctx context.Context, params PinChatMessageP) (bool, error) {
|
||||||
|
req := NewRequestWithChatID[bool]("pinChatMessage", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// UnpinChatMessageP holds parameters for the unpinChatMessage method.
|
// UnpinChatMessageP holds parameters for the unpinChatMessage method.
|
||||||
// See https://core.telegram.org/bots/api#unpinchatmessage
|
// See https://core.telegram.org/bots/api#unpinchatmessage
|
||||||
type UnpinChatMessageP struct {
|
type UnpinChatMessageP struct {
|
||||||
@@ -378,6 +558,14 @@ func (api *API) UnpinChatMessage(params UnpinChatMessageP) (bool, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnpinChatMessageWithContext is the context-aware variant of UnpinChatMessage.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#unpinchatmessage
|
||||||
|
func (api *API) UnpinChatMessageWithContext(ctx context.Context, params UnpinChatMessageP) (bool, error) {
|
||||||
|
req := NewRequestWithChatID[bool]("unpinChatMessage", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// UnpinAllChatMessagesP holds parameters for the unpinAllChatMessages method.
|
// UnpinAllChatMessagesP holds parameters for the unpinAllChatMessages method.
|
||||||
// See https://core.telegram.org/bots/api#unpinallchatmessages
|
// See https://core.telegram.org/bots/api#unpinallchatmessages
|
||||||
type UnpinAllChatMessagesP struct {
|
type UnpinAllChatMessagesP struct {
|
||||||
@@ -392,6 +580,14 @@ func (api *API) UnpinAllChatMessages(params UnpinAllChatMessagesP) (bool, error)
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnpinAllChatMessagesWithContext is the context-aware variant of UnpinAllChatMessages.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#unpinallchatmessages
|
||||||
|
func (api *API) UnpinAllChatMessagesWithContext(ctx context.Context, params UnpinAllChatMessagesP) (bool, error) {
|
||||||
|
req := NewRequestWithChatID[bool]("unpinAllChatMessages", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// LeaveChatP holds parameters for the leaveChat method.
|
// LeaveChatP holds parameters for the leaveChat method.
|
||||||
// See https://core.telegram.org/bots/api#leavechat
|
// See https://core.telegram.org/bots/api#leavechat
|
||||||
type LeaveChatP struct {
|
type LeaveChatP struct {
|
||||||
@@ -406,6 +602,14 @@ func (api *API) LeaveChat(params LeaveChatP) (bool, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LeaveChatWithContext is the context-aware variant of LeaveChat.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#leavechat
|
||||||
|
func (api *API) LeaveChatWithContext(ctx context.Context, params LeaveChatP) (bool, error) {
|
||||||
|
req := NewRequestWithChatID[bool]("leaveChat", params, params.ChatID) // fixed method name
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// GetChatP holds parameters for the getChat method.
|
// GetChatP holds parameters for the getChat method.
|
||||||
// See https://core.telegram.org/bots/api#getchat
|
// See https://core.telegram.org/bots/api#getchat
|
||||||
type GetChatP struct {
|
type GetChatP struct {
|
||||||
@@ -419,6 +623,14 @@ func (api *API) GetChat(params GetChatP) (ChatFullInfo, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetChatWithContext is the context-aware variant of GetChat.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#getchat
|
||||||
|
func (api *API) GetChatWithContext(ctx context.Context, params GetChatP) (ChatFullInfo, error) {
|
||||||
|
req := NewRequestWithChatID[ChatFullInfo]("getChat", params, params.ChatID) // fixed method name
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// GetChatAdministratorsP holds parameters for the getChatAdministrators method.
|
// GetChatAdministratorsP holds parameters for the getChatAdministrators method.
|
||||||
// See https://core.telegram.org/bots/api#getchatadministrators
|
// See https://core.telegram.org/bots/api#getchatadministrators
|
||||||
type GetChatAdministratorsP struct {
|
type GetChatAdministratorsP struct {
|
||||||
@@ -432,6 +644,14 @@ func (api *API) GetChatAdministrators(params GetChatAdministratorsP) ([]ChatMemb
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetChatAdministratorsWithContext is the context-aware variant of GetChatAdministrators.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#getchatadministrators
|
||||||
|
func (api *API) GetChatAdministratorsWithContext(ctx context.Context, params GetChatAdministratorsP) ([]ChatMember, error) {
|
||||||
|
req := NewRequestWithChatID[[]ChatMember]("getChatAdministrators", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// GetChatMembersCountP holds parameters for the getChatMemberCount method.
|
// GetChatMembersCountP holds parameters for the getChatMemberCount method.
|
||||||
// See https://core.telegram.org/bots/api#getchatmembercount
|
// See https://core.telegram.org/bots/api#getchatmembercount
|
||||||
type GetChatMembersCountP struct {
|
type GetChatMembersCountP struct {
|
||||||
@@ -445,11 +665,19 @@ func (api *API) GetChatMemberCount(params GetChatMembersCountP) (int, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetChatMemberCountWithContext is the context-aware variant of GetChatMemberCount.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#getchatmembercount
|
||||||
|
func (api *API) GetChatMemberCountWithContext(ctx context.Context, params GetChatMembersCountP) (int, error) {
|
||||||
|
req := NewRequestWithChatID[int]("getChatMemberCount", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// GetChatMemberP holds parameters for the getChatMember method.
|
// GetChatMemberP holds parameters for the getChatMember method.
|
||||||
// See https://core.telegram.org/bots/api#getchatmember
|
// See https://core.telegram.org/bots/api#getchatmember
|
||||||
type GetChatMemberP struct {
|
type GetChatMemberP struct {
|
||||||
ChatID int64 `json:"chat_id"`
|
ChatID int64 `json:"chat_id"`
|
||||||
UserID int `json:"user_id"`
|
UserID int64 `json:"user_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetChatMember returns information about a member of a chat.
|
// GetChatMember returns information about a member of a chat.
|
||||||
@@ -459,6 +687,14 @@ func (api *API) GetChatMember(params GetChatMemberP) (ChatMember, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetChatMemberWithContext is the context-aware variant of GetChatMember.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#getchatmember
|
||||||
|
func (api *API) GetChatMemberWithContext(ctx context.Context, params GetChatMemberP) (ChatMember, error) {
|
||||||
|
req := NewRequestWithChatID[ChatMember]("getChatMember", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// SetChatStickerSetP holds parameters for the setChatStickerSet method.
|
// SetChatStickerSetP holds parameters for the setChatStickerSet method.
|
||||||
// See https://core.telegram.org/bots/api#setchatstickerset
|
// See https://core.telegram.org/bots/api#setchatstickerset
|
||||||
type SetChatStickerSetP struct {
|
type SetChatStickerSetP struct {
|
||||||
@@ -474,6 +710,14 @@ func (api *API) SetChatStickerSet(params SetChatStickerSetP) (bool, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetChatStickerSetWithContext is the context-aware variant of SetChatStickerSet.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#setchatstickerset
|
||||||
|
func (api *API) SetChatStickerSetWithContext(ctx context.Context, params SetChatStickerSetP) (bool, error) {
|
||||||
|
req := NewRequestWithChatID[bool]("setChatStickerSet", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// DeleteChatStickerSetP holds parameters for the deleteChatStickerSet method.
|
// DeleteChatStickerSetP holds parameters for the deleteChatStickerSet method.
|
||||||
// See https://core.telegram.org/bots/api#deletechatstickerset
|
// See https://core.telegram.org/bots/api#deletechatstickerset
|
||||||
type DeleteChatStickerSetP struct {
|
type DeleteChatStickerSetP struct {
|
||||||
@@ -488,11 +732,19 @@ func (api *API) DeleteChatStickerSet(params DeleteChatStickerSetP) (bool, error)
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteChatStickerSetWithContext is the context-aware variant of DeleteChatStickerSet.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#deletechatstickerset
|
||||||
|
func (api *API) DeleteChatStickerSetWithContext(ctx context.Context, params DeleteChatStickerSetP) (bool, error) {
|
||||||
|
req := NewRequestWithChatID[bool]("deleteChatStickerSet", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// GetUserChatBoostsP holds parameters for the getUserChatBoosts method.
|
// GetUserChatBoostsP holds parameters for the getUserChatBoosts method.
|
||||||
// See https://core.telegram.org/bots/api#getuserchatboosts
|
// See https://core.telegram.org/bots/api#getuserchatboosts
|
||||||
type GetUserChatBoostsP struct {
|
type GetUserChatBoostsP struct {
|
||||||
ChatID int64 `json:"chat_id"`
|
ChatID int64 `json:"chat_id"`
|
||||||
UserID int `json:"user_id"`
|
UserID int64 `json:"user_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUserChatBoosts returns the list of boosts a user has given to a chat.
|
// GetUserChatBoosts returns the list of boosts a user has given to a chat.
|
||||||
@@ -502,6 +754,14 @@ func (api *API) GetUserChatBoosts(params GetUserChatBoostsP) (UserChatBoosts, er
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetUserChatBoostsWithContext is the context-aware variant of GetUserChatBoosts.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#getuserchatboosts
|
||||||
|
func (api *API) GetUserChatBoostsWithContext(ctx context.Context, params GetUserChatBoostsP) (UserChatBoosts, error) {
|
||||||
|
req := NewRequestWithChatID[UserChatBoosts]("getUserChatBoosts", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// GetChatGiftsP holds parameters for the getChatGifts method.
|
// GetChatGiftsP holds parameters for the getChatGifts method.
|
||||||
// See https://core.telegram.org/bots/api#getchatgifts
|
// See https://core.telegram.org/bots/api#getchatgifts
|
||||||
type GetChatGiftsP struct {
|
type GetChatGiftsP struct {
|
||||||
@@ -524,3 +784,11 @@ func (api *API) GetChatGifts(params GetChatGiftsP) (OwnedGifts, error) {
|
|||||||
req := NewRequestWithChatID[OwnedGifts]("getChatGifts", params, params.ChatID)
|
req := NewRequestWithChatID[OwnedGifts]("getChatGifts", params, params.ChatID)
|
||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetChatGiftsWithContext is the context-aware variant of GetChatGifts.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#getchatgifts
|
||||||
|
func (api *API) GetChatGiftsWithContext(ctx context.Context, params GetChatGiftsP) (OwnedGifts, error) {
|
||||||
|
req := NewRequestWithChatID[OwnedGifts]("getChatGifts", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|||||||
+4
-4
@@ -26,7 +26,7 @@ const (
|
|||||||
// ChatFullInfo contains full information about a chat.
|
// ChatFullInfo contains full information about a chat.
|
||||||
// See https://core.telegram.org/bots/api#chatfullinfo
|
// See https://core.telegram.org/bots/api#chatfullinfo
|
||||||
type ChatFullInfo struct {
|
type ChatFullInfo struct {
|
||||||
ID int `json:"id"`
|
ID int64 `json:"id"`
|
||||||
Type ChatType `json:"type"`
|
Type ChatType `json:"type"`
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
@@ -78,7 +78,7 @@ type ChatFullInfo struct {
|
|||||||
StickerSetName *string `json:"sticker_set_name,omitempty"`
|
StickerSetName *string `json:"sticker_set_name,omitempty"`
|
||||||
CanSetStickerSet *bool `json:"can_set_sticker_set,omitempty"`
|
CanSetStickerSet *bool `json:"can_set_sticker_set,omitempty"`
|
||||||
CustomEmojiStickerSetName *string `json:"custom_emoji_sticker_set_name,omitempty"`
|
CustomEmojiStickerSetName *string `json:"custom_emoji_sticker_set_name,omitempty"`
|
||||||
LinkedChatID *int `json:"linked_chat_id,omitempty"`
|
LinkedChatID *int64 `json:"linked_chat_id,omitempty"`
|
||||||
|
|
||||||
Location *ChatLocation `json:"location,omitempty"`
|
Location *ChatLocation `json:"location,omitempty"`
|
||||||
Rating *UserRating `json:"rating,omitempty"`
|
Rating *UserRating `json:"rating,omitempty"`
|
||||||
@@ -108,7 +108,7 @@ type ChatPermissions struct {
|
|||||||
CanSendPolls bool `json:"can_send_polls"`
|
CanSendPolls bool `json:"can_send_polls"`
|
||||||
CanSendOtherMessages bool `json:"can_send_other_messages"`
|
CanSendOtherMessages bool `json:"can_send_other_messages"`
|
||||||
CanAddWebPagePreview bool `json:"can_add_web_page_preview"`
|
CanAddWebPagePreview bool `json:"can_add_web_page_preview"`
|
||||||
CatEditTag bool `json:"cat_edit_tag"` // Note: field name likely a typo, should be "can_edit_tag"
|
CanEditTag bool `json:"can_edit_tag"`
|
||||||
CanChangeInfo bool `json:"can_change_info"`
|
CanChangeInfo bool `json:"can_change_info"`
|
||||||
CanInviteUsers bool `json:"can_invite_users"`
|
CanInviteUsers bool `json:"can_invite_users"`
|
||||||
CanPinMessages bool `json:"can_pin_messages"`
|
CanPinMessages bool `json:"can_pin_messages"`
|
||||||
@@ -127,7 +127,7 @@ type ChatLocation struct {
|
|||||||
type ChatInviteLink struct {
|
type ChatInviteLink struct {
|
||||||
InviteLink string `json:"invite_link"`
|
InviteLink string `json:"invite_link"`
|
||||||
Creator User `json:"creator"`
|
Creator User `json:"creator"`
|
||||||
CreateJoinRequest bool `json:"create_join_request"`
|
CreateJoinRequest bool `json:"creates_join_request"`
|
||||||
IsPrimary bool `json:"is_primary"`
|
IsPrimary bool `json:"is_primary"`
|
||||||
IsRevoked bool `json:"is_revoked"`
|
IsRevoked bool `json:"is_revoked"`
|
||||||
|
|
||||||
|
|||||||
@@ -5,3 +5,4 @@ import "errors"
|
|||||||
var ErrRateLimit = errors.New("rate limit exceeded")
|
var ErrRateLimit = errors.New("rate limit exceeded")
|
||||||
var ErrPoolUnexpected = errors.New("unexpected response from pool")
|
var ErrPoolUnexpected = errors.New("unexpected response from pool")
|
||||||
var ErrPoolQueueFull = errors.New("worker pool queue full")
|
var ErrPoolQueueFull = errors.New("worker pool queue full")
|
||||||
|
var ErrPoolStopped = errors.New("worker pool stopped")
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package tgapi
|
package tgapi
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
// BaseForumTopicP contains common fields for forum topic operations that require a chat ID and a message thread ID.
|
// BaseForumTopicP contains common fields for forum topic operations that require a chat ID and a message thread ID.
|
||||||
type BaseForumTopicP struct {
|
type BaseForumTopicP struct {
|
||||||
ChatID int64 `json:"chat_id"`
|
ChatID int64 `json:"chat_id"`
|
||||||
@@ -13,6 +15,14 @@ func (api *API) GetForumTopicIconStickers() ([]Sticker, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetForumTopicIconStickersWithContext is the context-aware variant of GetForumTopicIconStickers.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#getforumtopiciconstickers
|
||||||
|
func (api *API) GetForumTopicIconStickersWithContext(ctx context.Context) ([]Sticker, error) {
|
||||||
|
req := NewRequest[[]Sticker]("getForumTopicIconStickers", NoParams)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// CreateForumTopicP holds parameters for the createForumTopic method.
|
// CreateForumTopicP holds parameters for the createForumTopic method.
|
||||||
// See https://core.telegram.org/bots/api#createforumtopic
|
// See https://core.telegram.org/bots/api#createforumtopic
|
||||||
type CreateForumTopicP struct {
|
type CreateForumTopicP struct {
|
||||||
@@ -30,6 +40,14 @@ func (api *API) CreateForumTopic(params CreateForumTopicP) (ForumTopic, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateForumTopicWithContext is the context-aware variant of CreateForumTopic.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#createforumtopic
|
||||||
|
func (api *API) CreateForumTopicWithContext(ctx context.Context, params CreateForumTopicP) (ForumTopic, error) {
|
||||||
|
req := NewRequestWithChatID[ForumTopic]("createForumTopic", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// EditForumTopicP holds parameters for the editForumTopic method.
|
// EditForumTopicP holds parameters for the editForumTopic method.
|
||||||
// See https://core.telegram.org/bots/api#editforumtopic
|
// See https://core.telegram.org/bots/api#editforumtopic
|
||||||
type EditForumTopicP struct {
|
type EditForumTopicP struct {
|
||||||
@@ -46,6 +64,14 @@ func (api *API) EditForumTopic(params EditForumTopicP) (bool, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EditForumTopicWithContext is the context-aware variant of EditForumTopic.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#editforumtopic
|
||||||
|
func (api *API) EditForumTopicWithContext(ctx context.Context, params EditForumTopicP) (bool, error) {
|
||||||
|
req := NewRequestWithChatID[bool]("editForumTopic", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// CloseForumTopic closes an open forum topic.
|
// CloseForumTopic closes an open forum topic.
|
||||||
// Returns True on success.
|
// Returns True on success.
|
||||||
// See https://core.telegram.org/bots/api#closeforumtopic
|
// See https://core.telegram.org/bots/api#closeforumtopic
|
||||||
@@ -54,6 +80,14 @@ func (api *API) CloseForumTopic(params BaseForumTopicP) (bool, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CloseForumTopicWithContext is the context-aware variant of CloseForumTopic.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#closeforumtopic
|
||||||
|
func (api *API) CloseForumTopicWithContext(ctx context.Context, params BaseForumTopicP) (bool, error) {
|
||||||
|
req := NewRequestWithChatID[bool]("closeForumTopic", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// ReopenForumTopic reopens a closed forum topic.
|
// ReopenForumTopic reopens a closed forum topic.
|
||||||
// Returns True on success.
|
// Returns True on success.
|
||||||
// See https://core.telegram.org/bots/api#reopenforumtopic
|
// See https://core.telegram.org/bots/api#reopenforumtopic
|
||||||
@@ -62,6 +96,14 @@ func (api *API) ReopenForumTopic(params BaseForumTopicP) (bool, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReopenForumTopicWithContext is the context-aware variant of ReopenForumTopic.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#reopenforumtopic
|
||||||
|
func (api *API) ReopenForumTopicWithContext(ctx context.Context, params BaseForumTopicP) (bool, error) {
|
||||||
|
req := NewRequestWithChatID[bool]("reopenForumTopic", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// DeleteForumTopic deletes a forum topic.
|
// DeleteForumTopic deletes a forum topic.
|
||||||
// Returns True on success.
|
// Returns True on success.
|
||||||
// See https://core.telegram.org/bots/api#deleteforumtopic
|
// See https://core.telegram.org/bots/api#deleteforumtopic
|
||||||
@@ -70,6 +112,14 @@ func (api *API) DeleteForumTopic(params BaseForumTopicP) (bool, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteForumTopicWithContext is the context-aware variant of DeleteForumTopic.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#deleteforumtopic
|
||||||
|
func (api *API) DeleteForumTopicWithContext(ctx context.Context, params BaseForumTopicP) (bool, error) {
|
||||||
|
req := NewRequestWithChatID[bool]("deleteForumTopic", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// UnpinAllForumTopicMessages clears the list of pinned messages in a forum topic.
|
// UnpinAllForumTopicMessages clears the list of pinned messages in a forum topic.
|
||||||
// Returns True on success.
|
// Returns True on success.
|
||||||
// See https://core.telegram.org/bots/api#unpinallforumtopicmessages
|
// See https://core.telegram.org/bots/api#unpinallforumtopicmessages
|
||||||
@@ -78,6 +128,14 @@ func (api *API) UnpinAllForumTopicMessages(params BaseForumTopicP) (bool, error)
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnpinAllForumTopicMessagesWithContext is the context-aware variant of UnpinAllForumTopicMessages.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#unpinallforumtopicmessages
|
||||||
|
func (api *API) UnpinAllForumTopicMessagesWithContext(ctx context.Context, params BaseForumTopicP) (bool, error) {
|
||||||
|
req := NewRequestWithChatID[bool]("unpinAllForumTopicMessages", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// BaseGeneralForumTopicP contains common fields for general forum topic operations that require a chat ID.
|
// BaseGeneralForumTopicP contains common fields for general forum topic operations that require a chat ID.
|
||||||
type BaseGeneralForumTopicP struct {
|
type BaseGeneralForumTopicP struct {
|
||||||
ChatID int64 `json:"chat_id"`
|
ChatID int64 `json:"chat_id"`
|
||||||
@@ -98,6 +156,14 @@ func (api *API) EditGeneralForumTopic(params EditGeneralForumTopicP) (bool, erro
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EditGeneralForumTopicWithContext is the context-aware variant of EditGeneralForumTopic.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#editgeneralforumtopic
|
||||||
|
func (api *API) EditGeneralForumTopicWithContext(ctx context.Context, params EditGeneralForumTopicP) (bool, error) {
|
||||||
|
req := NewRequestWithChatID[bool]("editGeneralForumTopic", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// CloseGeneralForumTopic closes the 'General' topic in a forum supergroup.
|
// CloseGeneralForumTopic closes the 'General' topic in a forum supergroup.
|
||||||
// Returns True on success.
|
// Returns True on success.
|
||||||
// See https://core.telegram.org/bots/api#closegeneralforumtopic
|
// See https://core.telegram.org/bots/api#closegeneralforumtopic
|
||||||
@@ -106,6 +172,14 @@ func (api *API) CloseGeneralForumTopic(params BaseGeneralForumTopicP) (bool, err
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CloseGeneralForumTopicWithContext is the context-aware variant of CloseGeneralForumTopic.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#closegeneralforumtopic
|
||||||
|
func (api *API) CloseGeneralForumTopicWithContext(ctx context.Context, params BaseGeneralForumTopicP) (bool, error) {
|
||||||
|
req := NewRequestWithChatID[bool]("closeGeneralForumTopic", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// ReopenGeneralForumTopic reopens the 'General' topic in a forum supergroup.
|
// ReopenGeneralForumTopic reopens the 'General' topic in a forum supergroup.
|
||||||
// Returns True on success.
|
// Returns True on success.
|
||||||
// See https://core.telegram.org/bots/api#reopengeneralforumtopic
|
// See https://core.telegram.org/bots/api#reopengeneralforumtopic
|
||||||
@@ -114,6 +188,14 @@ func (api *API) ReopenGeneralForumTopic(params BaseGeneralForumTopicP) (bool, er
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReopenGeneralForumTopicWithContext is the context-aware variant of ReopenGeneralForumTopic.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#reopengeneralforumtopic
|
||||||
|
func (api *API) ReopenGeneralForumTopicWithContext(ctx context.Context, params BaseGeneralForumTopicP) (bool, error) {
|
||||||
|
req := NewRequestWithChatID[bool]("reopenGeneralForumTopic", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// HideGeneralForumTopic hides the 'General' topic in a forum supergroup.
|
// HideGeneralForumTopic hides the 'General' topic in a forum supergroup.
|
||||||
// Returns True on success.
|
// Returns True on success.
|
||||||
// See https://core.telegram.org/bots/api#hidegeneralforumtopic
|
// See https://core.telegram.org/bots/api#hidegeneralforumtopic
|
||||||
@@ -122,6 +204,14 @@ func (api *API) HideGeneralForumTopic(params BaseGeneralForumTopicP) (bool, erro
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HideGeneralForumTopicWithContext is the context-aware variant of HideGeneralForumTopic.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#hidegeneralforumtopic
|
||||||
|
func (api *API) HideGeneralForumTopicWithContext(ctx context.Context, params BaseGeneralForumTopicP) (bool, error) {
|
||||||
|
req := NewRequestWithChatID[bool]("hideGeneralForumTopic", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// UnhideGeneralForumTopic unhides the 'General' topic in a forum supergroup.
|
// UnhideGeneralForumTopic unhides the 'General' topic in a forum supergroup.
|
||||||
// Returns True on success.
|
// Returns True on success.
|
||||||
// See https://core.telegram.org/bots/api#unhidegeneralforumtopic
|
// See https://core.telegram.org/bots/api#unhidegeneralforumtopic
|
||||||
@@ -130,6 +220,14 @@ func (api *API) UnhideGeneralForumTopic(params BaseGeneralForumTopicP) (bool, er
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnhideGeneralForumTopicWithContext is the context-aware variant of UnhideGeneralForumTopic.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#unhidegeneralforumtopic
|
||||||
|
func (api *API) UnhideGeneralForumTopicWithContext(ctx context.Context, params BaseGeneralForumTopicP) (bool, error) {
|
||||||
|
req := NewRequestWithChatID[bool]("unhideGeneralForumTopic", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// UnpinAllGeneralForumTopicMessages clears the list of pinned messages in the 'General' topic.
|
// UnpinAllGeneralForumTopicMessages clears the list of pinned messages in the 'General' topic.
|
||||||
// Returns True on success.
|
// Returns True on success.
|
||||||
// See https://core.telegram.org/bots/api#unpinallgeneralforumtopicmessages
|
// See https://core.telegram.org/bots/api#unpinallgeneralforumtopicmessages
|
||||||
@@ -137,3 +235,11 @@ func (api *API) UnpinAllGeneralForumTopicMessages(params BaseGeneralForumTopicP)
|
|||||||
req := NewRequestWithChatID[bool]("unpinAllGeneralForumTopicMessages", params, params.ChatID)
|
req := NewRequestWithChatID[bool]("unpinAllGeneralForumTopicMessages", params, params.ChatID)
|
||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnpinAllGeneralForumTopicMessagesWithContext is the context-aware variant of UnpinAllGeneralForumTopicMessages.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#unpinallgeneralforumtopicmessages
|
||||||
|
func (api *API) UnpinAllGeneralForumTopicMessagesWithContext(ctx context.Context, params BaseGeneralForumTopicP) (bool, error) {
|
||||||
|
req := NewRequestWithChatID[bool]("unpinAllGeneralForumTopicMessages", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,102 @@
|
|||||||
|
package tgapi
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
|
// SendGameP holds parameters for the sendGame method.
|
||||||
|
// See https://core.telegram.org/bots/api#sendgame
|
||||||
|
type SendGameP struct {
|
||||||
|
BusinessConnectionID string `json:"business_connection_id,omitempty"`
|
||||||
|
ChatID int64 `json:"chat_id"`
|
||||||
|
MessageThreadID int `json:"message_thread_id,omitempty"`
|
||||||
|
|
||||||
|
GameShortName string `json:"game_short_name"`
|
||||||
|
|
||||||
|
DisableNotification bool `json:"disable_notification,omitempty"`
|
||||||
|
ProtectContent bool `json:"protect_content,omitempty"`
|
||||||
|
AllowPaidBroadcast bool `json:"allow_paid_broadcast,omitempty"`
|
||||||
|
MessageEffectID string `json:"message_effect_id,omitempty"`
|
||||||
|
ReplyParameters *ReplyParameters `json:"reply_parameters,omitempty"`
|
||||||
|
ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendGame sends a game message.
|
||||||
|
// See https://core.telegram.org/bots/api#sendgame
|
||||||
|
func (api *API) SendGame(params SendGameP) (Message, error) {
|
||||||
|
req := NewRequestWithChatID[Message]("sendGame", params, params.ChatID)
|
||||||
|
return req.Do(api)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendGameWithContext is the context-aware variant of SendGame.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#sendgame
|
||||||
|
func (api *API) SendGameWithContext(ctx context.Context, params SendGameP) (Message, error) {
|
||||||
|
req := NewRequestWithChatID[Message]("sendGame", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetGameScoreP holds parameters for the setGameScore method.
|
||||||
|
// See https://core.telegram.org/bots/api#setgamescore
|
||||||
|
type SetGameScoreP struct {
|
||||||
|
UserID int64 `json:"user_id"`
|
||||||
|
Score int `json:"score"`
|
||||||
|
Force bool `json:"force,omitempty"`
|
||||||
|
DisableEditMessage bool `json:"disable_edit_message,omitempty"`
|
||||||
|
ChatID int64 `json:"chat_id,omitempty"`
|
||||||
|
MessageID int `json:"message_id,omitempty"`
|
||||||
|
InlineMessageID string `json:"inline_message_id,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetGameScore sets a user's score in a game message.
|
||||||
|
// If inline_message_id is provided, returns a boolean success flag.
|
||||||
|
// Otherwise returns the edited Message.
|
||||||
|
// See https://core.telegram.org/bots/api#setgamescore
|
||||||
|
func (api *API) SetGameScore(params SetGameScoreP) (Message, bool, error) {
|
||||||
|
var zero Message
|
||||||
|
if params.InlineMessageID != "" {
|
||||||
|
req := NewRequestWithChatID[bool]("setGameScore", params, params.ChatID)
|
||||||
|
res, err := req.Do(api)
|
||||||
|
return zero, res, err
|
||||||
|
}
|
||||||
|
req := NewRequestWithChatID[Message]("setGameScore", params, params.ChatID)
|
||||||
|
res, err := req.Do(api)
|
||||||
|
return res, false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetGameScoreWithContext is the context-aware variant of SetGameScore.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#setgamescore
|
||||||
|
func (api *API) SetGameScoreWithContext(ctx context.Context, params SetGameScoreP) (Message, bool, error) {
|
||||||
|
var zero Message
|
||||||
|
if params.InlineMessageID != "" {
|
||||||
|
req := NewRequestWithChatID[bool]("setGameScore", params, params.ChatID)
|
||||||
|
res, err := req.DoWithContext(ctx, api)
|
||||||
|
return zero, res, err
|
||||||
|
}
|
||||||
|
req := NewRequestWithChatID[Message]("setGameScore", params, params.ChatID)
|
||||||
|
res, err := req.DoWithContext(ctx, api)
|
||||||
|
return res, false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetGameHighScoresP holds parameters for the getGameHighScores method.
|
||||||
|
// See https://core.telegram.org/bots/api#getgamehighscores
|
||||||
|
type GetGameHighScoresP struct {
|
||||||
|
UserID int64 `json:"user_id"`
|
||||||
|
ChatID int64 `json:"chat_id,omitempty"`
|
||||||
|
MessageID int `json:"message_id,omitempty"`
|
||||||
|
InlineMessageID string `json:"inline_message_id,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetGameHighScores returns game high score data for a user.
|
||||||
|
// See https://core.telegram.org/bots/api#getgamehighscores
|
||||||
|
func (api *API) GetGameHighScores(params GetGameHighScoresP) ([]GameHighScore, error) {
|
||||||
|
req := NewRequestWithChatID[[]GameHighScore]("getGameHighScores", params, params.ChatID)
|
||||||
|
return req.Do(api)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetGameHighScoresWithContext is the context-aware variant of GetGameHighScores.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#getgamehighscores
|
||||||
|
func (api *API) GetGameHighScoresWithContext(ctx context.Context, params GetGameHighScoresP) ([]GameHighScore, error) {
|
||||||
|
req := NewRequestWithChatID[[]GameHighScore]("getGameHighScores", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package tgapi
|
||||||
|
|
||||||
|
// GameHighScore represents one row in a game high score table.
|
||||||
|
// See https://core.telegram.org/bots/api#gamehighscore
|
||||||
|
type GameHighScore struct {
|
||||||
|
Position int `json:"position"`
|
||||||
|
User User `json:"user"`
|
||||||
|
Score int `json:"score"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
package tgapi
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
|
// AnswerInlineQueryP holds parameters for the answerInlineQuery method.
|
||||||
|
// See https://core.telegram.org/bots/api#answerinlinequery
|
||||||
|
type AnswerInlineQueryP struct {
|
||||||
|
InlineQueryID string `json:"inline_query_id"`
|
||||||
|
Results []InlineQueryResult `json:"results"`
|
||||||
|
CacheTime int `json:"cache_time,omitempty"`
|
||||||
|
IsPersonal bool `json:"is_personal,omitempty"`
|
||||||
|
NextOffset string `json:"next_offset,omitempty"`
|
||||||
|
Button *InlineQueryResultsButton `json:"button,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AnswerInlineQuery sends answers to an inline query.
|
||||||
|
// Returns true on success.
|
||||||
|
// See https://core.telegram.org/bots/api#answerinlinequery
|
||||||
|
func (api *API) AnswerInlineQuery(params AnswerInlineQueryP) (bool, error) {
|
||||||
|
req := NewRequest[bool]("answerInlineQuery", params)
|
||||||
|
return req.Do(api)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AnswerInlineQueryWithContext is the context-aware variant of AnswerInlineQuery.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#answerinlinequery
|
||||||
|
func (api *API) AnswerInlineQueryWithContext(ctx context.Context, params AnswerInlineQueryP) (bool, error) {
|
||||||
|
req := NewRequest[bool]("answerInlineQuery", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AnswerWebAppQueryP holds parameters for the answerWebAppQuery method.
|
||||||
|
// See https://core.telegram.org/bots/api#answerwebappquery
|
||||||
|
type AnswerWebAppQueryP struct {
|
||||||
|
WebAppQueryID string `json:"web_app_query_id"`
|
||||||
|
Result InlineQueryResult `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AnswerWebAppQuery sets the result of a Web App interaction.
|
||||||
|
// See https://core.telegram.org/bots/api#answerwebappquery
|
||||||
|
func (api *API) AnswerWebAppQuery(params AnswerWebAppQueryP) (SentWebAppMessage, error) {
|
||||||
|
req := NewRequest[SentWebAppMessage]("answerWebAppQuery", params)
|
||||||
|
return req.Do(api)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AnswerWebAppQueryWithContext is the context-aware variant of AnswerWebAppQuery.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#answerwebappquery
|
||||||
|
func (api *API) AnswerWebAppQueryWithContext(ctx context.Context, params AnswerWebAppQueryP) (SentWebAppMessage, error) {
|
||||||
|
req := NewRequest[SentWebAppMessage]("answerWebAppQuery", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SavePreparedInlineMessageP holds parameters for the savePreparedInlineMessage method.
|
||||||
|
// See https://core.telegram.org/bots/api#savepreparedinlinemessage
|
||||||
|
type SavePreparedInlineMessageP struct {
|
||||||
|
UserID int64 `json:"user_id"`
|
||||||
|
Result InlineQueryResult `json:"result"`
|
||||||
|
AllowUserChats bool `json:"allow_user_chats,omitempty"`
|
||||||
|
AllowBotChats bool `json:"allow_bot_chats,omitempty"`
|
||||||
|
AllowGroupChats bool `json:"allow_group_chats,omitempty"`
|
||||||
|
AllowChannelChats bool `json:"allow_channel_chats,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SavePreparedInlineMessage stores a prepared message for Mini App users.
|
||||||
|
// See https://core.telegram.org/bots/api#savepreparedinlinemessage
|
||||||
|
func (api *API) SavePreparedInlineMessage(params SavePreparedInlineMessageP) (PreparedInlineMessage, error) {
|
||||||
|
req := NewRequest[PreparedInlineMessage]("savePreparedInlineMessage", params)
|
||||||
|
return req.Do(api)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SavePreparedInlineMessageWithContext is the context-aware variant of SavePreparedInlineMessage.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#savepreparedinlinemessage
|
||||||
|
func (api *API) SavePreparedInlineMessageWithContext(ctx context.Context, params SavePreparedInlineMessageP) (PreparedInlineMessage, error) {
|
||||||
|
req := NewRequest[PreparedInlineMessage]("savePreparedInlineMessage", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package tgapi
|
||||||
|
|
||||||
|
// InlineQueryResult is a JSON-serializable inline query result object.
|
||||||
|
// See https://core.telegram.org/bots/api#inlinequeryresult
|
||||||
|
type InlineQueryResult map[string]any
|
||||||
|
|
||||||
|
// InlineQueryResultsButton represents a button shown above inline query results.
|
||||||
|
// See https://core.telegram.org/bots/api#inlinequeryresultsbutton
|
||||||
|
type InlineQueryResultsButton struct {
|
||||||
|
Text string `json:"text"`
|
||||||
|
WebApp *WebAppInfo `json:"web_app,omitempty"`
|
||||||
|
StartParameter string `json:"start_parameter,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SentWebAppMessage describes an inline message sent by a Web App on behalf of a user.
|
||||||
|
// See https://core.telegram.org/bots/api#sentwebappmessage
|
||||||
|
type SentWebAppMessage struct {
|
||||||
|
InlineMessageID string `json:"inline_message_id,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PreparedInlineMessage describes a prepared inline message.
|
||||||
|
// See https://core.telegram.org/bots/api#preparedinlinemessage
|
||||||
|
type PreparedInlineMessage struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
ExpirationDate int `json:"expiration_date"`
|
||||||
|
}
|
||||||
+321
-48
@@ -1,5 +1,7 @@
|
|||||||
package tgapi
|
package tgapi
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
// SendMessageP holds parameters for the sendMessage method.
|
// SendMessageP holds parameters for the sendMessage method.
|
||||||
// See https://core.telegram.org/bots/api#sendmessage
|
// See https://core.telegram.org/bots/api#sendmessage
|
||||||
type SendMessageP struct {
|
type SendMessageP struct {
|
||||||
@@ -12,7 +14,7 @@ type SendMessageP struct {
|
|||||||
ParseMode ParseMode `json:"parse_mode,omitempty"`
|
ParseMode ParseMode `json:"parse_mode,omitempty"`
|
||||||
Entities []MessageEntity `json:"entities,omitempty"`
|
Entities []MessageEntity `json:"entities,omitempty"`
|
||||||
LinkPreviewOptions *LinkPreviewOptions `json:"link_preview_options,omitempty"`
|
LinkPreviewOptions *LinkPreviewOptions `json:"link_preview_options,omitempty"`
|
||||||
DisableNotifications bool `json:"disable_notifications,omitempty"`
|
DisableNotifications bool `json:"disable_notification,omitempty"`
|
||||||
ProtectContent bool `json:"protect_content,omitempty"`
|
ProtectContent bool `json:"protect_content,omitempty"`
|
||||||
AllowPaidBroadcast bool `json:"allow_paid_broadcast,omitempty"`
|
AllowPaidBroadcast bool `json:"allow_paid_broadcast,omitempty"`
|
||||||
MessageEffectID string `json:"message_effect_id,omitempty"`
|
MessageEffectID string `json:"message_effect_id,omitempty"`
|
||||||
@@ -29,6 +31,14 @@ func (api *API) SendMessage(params SendMessageP) (Message, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SendMessageWithContext is the context-aware variant of SendMessage.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#sendmessage
|
||||||
|
func (api *API) SendMessageWithContext(ctx context.Context, params SendMessageP) (Message, error) {
|
||||||
|
req := NewRequestWithChatID[Message, SendMessageP]("sendMessage", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// ForwardMessageP holds parameters for the forwardMessage method.
|
// ForwardMessageP holds parameters for the forwardMessage method.
|
||||||
// See https://core.telegram.org/bots/api#forwardmessage
|
// See https://core.telegram.org/bots/api#forwardmessage
|
||||||
type ForwardMessageP struct {
|
type ForwardMessageP struct {
|
||||||
@@ -53,6 +63,14 @@ func (api *API) ForwardMessage(params ForwardMessageP) (Message, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ForwardMessageWithContext is the context-aware variant of ForwardMessage.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#forwardmessage
|
||||||
|
func (api *API) ForwardMessageWithContext(ctx context.Context, params ForwardMessageP) (Message, error) {
|
||||||
|
req := NewRequestWithChatID[Message]("forwardMessage", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// ForwardMessagesP holds parameters for the forwardMessages method.
|
// ForwardMessagesP holds parameters for the forwardMessages method.
|
||||||
// See https://core.telegram.org/bots/api#forwardmessages
|
// See https://core.telegram.org/bots/api#forwardmessages
|
||||||
type ForwardMessagesP struct {
|
type ForwardMessagesP struct {
|
||||||
@@ -69,11 +87,19 @@ type ForwardMessagesP struct {
|
|||||||
// ForwardMessages forwards multiple messages.
|
// ForwardMessages forwards multiple messages.
|
||||||
// Returns an array of message IDs of the sent messages.
|
// Returns an array of message IDs of the sent messages.
|
||||||
// See https://core.telegram.org/bots/api#forwardmessages
|
// See https://core.telegram.org/bots/api#forwardmessages
|
||||||
func (api *API) ForwardMessages(params ForwardMessagesP) ([]int, error) {
|
func (api *API) ForwardMessages(params ForwardMessagesP) ([]MessageID, error) {
|
||||||
req := NewRequestWithChatID[[]int]("forwardMessages", params, params.ChatID)
|
req := NewRequestWithChatID[[]MessageID]("forwardMessages", params, params.ChatID)
|
||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ForwardMessagesWithContext is the context-aware variant of ForwardMessages.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#forwardmessages
|
||||||
|
func (api *API) ForwardMessagesWithContext(ctx context.Context, params ForwardMessagesP) ([]MessageID, error) {
|
||||||
|
req := NewRequestWithChatID[[]MessageID]("forwardMessages", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// CopyMessageP holds parameters for the copyMessage method.
|
// CopyMessageP holds parameters for the copyMessage method.
|
||||||
// See https://core.telegram.org/bots/api#copymessage
|
// See https://core.telegram.org/bots/api#copymessage
|
||||||
type CopyMessageP struct {
|
type CopyMessageP struct {
|
||||||
@@ -103,8 +129,22 @@ type CopyMessageP struct {
|
|||||||
// Returns the MessageID of the sent copy.
|
// Returns the MessageID of the sent copy.
|
||||||
// See https://core.telegram.org/bots/api#copymessage
|
// See https://core.telegram.org/bots/api#copymessage
|
||||||
func (api *API) CopyMessage(params CopyMessageP) (int, error) {
|
func (api *API) CopyMessage(params CopyMessageP) (int, error) {
|
||||||
req := NewRequestWithChatID[int]("copyMessage", params, params.ChatID)
|
msgID, err := NewRequestWithChatID[MessageID]("copyMessage", params, params.ChatID).Do(api)
|
||||||
return req.Do(api)
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return msgID.MessageID, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CopyMessageWithContext is the context-aware variant of CopyMessage.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#copymessage
|
||||||
|
func (api *API) CopyMessageWithContext(ctx context.Context, params CopyMessageP) (int, error) {
|
||||||
|
msgID, err := NewRequestWithChatID[MessageID]("copyMessage", params, params.ChatID).DoWithContext(ctx, api)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return msgID.MessageID, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CopyMessagesP holds parameters for the copyMessages method.
|
// CopyMessagesP holds parameters for the copyMessages method.
|
||||||
@@ -124,18 +164,26 @@ type CopyMessagesP struct {
|
|||||||
// CopyMessages copies multiple messages.
|
// CopyMessages copies multiple messages.
|
||||||
// Returns an array of message IDs of the sent copies.
|
// Returns an array of message IDs of the sent copies.
|
||||||
// See https://core.telegram.org/bots/api#copymessages
|
// See https://core.telegram.org/bots/api#copymessages
|
||||||
func (api *API) CopyMessages(params CopyMessagesP) ([]int, error) {
|
func (api *API) CopyMessages(params CopyMessagesP) ([]MessageID, error) {
|
||||||
req := NewRequestWithChatID[[]int]("copyMessages", params, params.ChatID)
|
req := NewRequestWithChatID[[]MessageID]("copyMessages", params, params.ChatID)
|
||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CopyMessagesWithContext is the context-aware variant of CopyMessages.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#copymessages
|
||||||
|
func (api *API) CopyMessagesWithContext(ctx context.Context, params CopyMessagesP) ([]MessageID, error) {
|
||||||
|
req := NewRequestWithChatID[[]MessageID]("copyMessages", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// SendLocationP holds parameters for the sendLocation method.
|
// SendLocationP holds parameters for the sendLocation method.
|
||||||
// See https://core.telegram.org/bots/api#sendlocation
|
// See https://core.telegram.org/bots/api#sendlocation
|
||||||
type SendLocationP struct {
|
type SendLocationP struct {
|
||||||
BusinessConnectionID int `json:"business_connection_id,omitempty"`
|
BusinessConnectionID string `json:"business_connection_id,omitempty"`
|
||||||
ChatID int64 `json:"chat_id"`
|
ChatID int64 `json:"chat_id"`
|
||||||
MessageThreadID int `json:"message_thread_id,omitempty"`
|
MessageThreadID int `json:"message_thread_id,omitempty"`
|
||||||
DirectMessagesTopicID int `json:"direct_messages_topic_id,omitempty"`
|
DirectMessagesTopicID int `json:"direct_messages_topic_id,omitempty"`
|
||||||
|
|
||||||
Latitude float64 `json:"latitude"`
|
Latitude float64 `json:"latitude"`
|
||||||
Longitude float64 `json:"longitude"`
|
Longitude float64 `json:"longitude"`
|
||||||
@@ -161,13 +209,21 @@ func (api *API) SendLocation(params SendLocationP) (Message, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SendLocationWithContext is the context-aware variant of SendLocation.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#sendlocation
|
||||||
|
func (api *API) SendLocationWithContext(ctx context.Context, params SendLocationP) (Message, error) {
|
||||||
|
req := NewRequestWithChatID[Message]("sendLocation", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// SendVenueP holds parameters for the sendVenue method.
|
// SendVenueP holds parameters for the sendVenue method.
|
||||||
// See https://core.telegram.org/bots/api#sendvenue
|
// See https://core.telegram.org/bots/api#sendvenue
|
||||||
type SendVenueP struct {
|
type SendVenueP struct {
|
||||||
BusinessConnectionID int `json:"business_connection_id,omitempty"`
|
BusinessConnectionID string `json:"business_connection_id,omitempty"`
|
||||||
ChatID int64 `json:"chat_id"`
|
ChatID int64 `json:"chat_id"`
|
||||||
MessageThreadID int `json:"message_thread_id,omitempty"`
|
MessageThreadID int `json:"message_thread_id,omitempty"`
|
||||||
DirectMessagesTopicID int `json:"direct_messages_topic_id,omitempty"`
|
DirectMessagesTopicID int `json:"direct_messages_topic_id,omitempty"`
|
||||||
|
|
||||||
Latitude float64 `json:"latitude"`
|
Latitude float64 `json:"latitude"`
|
||||||
Longitude float64 `json:"longitude"`
|
Longitude float64 `json:"longitude"`
|
||||||
@@ -195,13 +251,21 @@ func (api *API) SendVenue(params SendVenueP) (Message, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SendVenueWithContext is the context-aware variant of SendVenue.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#sendvenue
|
||||||
|
func (api *API) SendVenueWithContext(ctx context.Context, params SendVenueP) (Message, error) {
|
||||||
|
req := NewRequestWithChatID[Message]("sendVenue", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// SendContactP holds parameters for the sendContact method.
|
// SendContactP holds parameters for the sendContact method.
|
||||||
// See https://core.telegram.org/bots/api#sendcontact
|
// See https://core.telegram.org/bots/api#sendcontact
|
||||||
type SendContactP struct {
|
type SendContactP struct {
|
||||||
BusinessConnectionID int `json:"business_connection_id,omitempty"`
|
BusinessConnectionID string `json:"business_connection_id,omitempty"`
|
||||||
ChatID int64 `json:"chat_id"`
|
ChatID int64 `json:"chat_id"`
|
||||||
MessageThreadID int `json:"message_thread_id,omitempty"`
|
MessageThreadID int `json:"message_thread_id,omitempty"`
|
||||||
DirectMessagesTopicID int `json:"direct_messages_topic_id,omitempty"`
|
DirectMessagesTopicID int `json:"direct_messages_topic_id,omitempty"`
|
||||||
|
|
||||||
PhoneNumber string `json:"phone_number"`
|
PhoneNumber string `json:"phone_number"`
|
||||||
FirstName string `json:"first_name"`
|
FirstName string `json:"first_name"`
|
||||||
@@ -225,15 +289,23 @@ func (api *API) SendContact(params SendContactP) (Message, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SendContactWithContext is the context-aware variant of SendContact.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#sendcontact
|
||||||
|
func (api *API) SendContactWithContext(ctx context.Context, params SendContactP) (Message, error) {
|
||||||
|
req := NewRequestWithChatID[Message]("sendContact", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// SendPollP holds parameters for the sendPoll method.
|
// SendPollP holds parameters for the sendPoll method.
|
||||||
// See https://core.telegram.org/bots/api#sendpoll
|
// See https://core.telegram.org/bots/api#sendpoll
|
||||||
type SendPollP struct {
|
type SendPollP struct {
|
||||||
BusinessConnectionID int `json:"business_connection_id,omitempty"`
|
BusinessConnectionID string `json:"business_connection_id,omitempty"`
|
||||||
ChatID int64 `json:"chat_id"`
|
ChatID int64 `json:"chat_id"`
|
||||||
MessageThreadID int `json:"message_thread_id,omitempty"`
|
MessageThreadID int `json:"message_thread_id,omitempty"`
|
||||||
|
|
||||||
Question string `json:"question"`
|
Question string `json:"question"`
|
||||||
QuestionParseMode ParseMode `json:"question_mode,omitempty"`
|
QuestionParseMode ParseMode `json:"question_parse_mode,omitempty"`
|
||||||
QuestionEntities []MessageEntity `json:"question_entities,omitempty"`
|
QuestionEntities []MessageEntity `json:"question_entities,omitempty"`
|
||||||
Options []InputPollOption `json:"options"`
|
Options []InputPollOption `json:"options"`
|
||||||
IsAnonymous bool `json:"is_anonymous,omitempty"`
|
IsAnonymous bool `json:"is_anonymous,omitempty"`
|
||||||
@@ -263,10 +335,18 @@ func (api *API) SendPoll(params SendPollP) (Message, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SendPollWithContext is the context-aware variant of SendPoll.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#sendpoll
|
||||||
|
func (api *API) SendPollWithContext(ctx context.Context, params SendPollP) (Message, error) {
|
||||||
|
req := NewRequestWithChatID[Message]("sendPoll", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// SendChecklistP holds parameters for the sendChecklist method.
|
// SendChecklistP holds parameters for the sendChecklist method.
|
||||||
// See https://core.telegram.org/bots/api#sendchecklist
|
// See https://core.telegram.org/bots/api#sendchecklist
|
||||||
type SendChecklistP struct {
|
type SendChecklistP struct {
|
||||||
BusinessConnectionID int `json:"business_connection_id"`
|
BusinessConnectionID string `json:"business_connection_id"`
|
||||||
ChatID int64 `json:"chat_id"`
|
ChatID int64 `json:"chat_id"`
|
||||||
Checklist InputChecklist `json:"checklist"`
|
Checklist InputChecklist `json:"checklist"`
|
||||||
|
|
||||||
@@ -285,13 +365,21 @@ func (api *API) SendChecklist(params SendChecklistP) (Message, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SendChecklistWithContext is the context-aware variant of SendChecklist.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#sendchecklist
|
||||||
|
func (api *API) SendChecklistWithContext(ctx context.Context, params SendChecklistP) (Message, error) {
|
||||||
|
req := NewRequestWithChatID[Message]("sendChecklist", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// SendDiceP holds parameters for the sendDice method.
|
// SendDiceP holds parameters for the sendDice method.
|
||||||
// See https://core.telegram.org/bots/api#senddice
|
// See https://core.telegram.org/bots/api#senddice
|
||||||
type SendDiceP struct {
|
type SendDiceP struct {
|
||||||
BusinessConnectionID int `json:"business_connection_id,omitempty"`
|
BusinessConnectionID string `json:"business_connection_id,omitempty"`
|
||||||
ChatID int64 `json:"chat_id"`
|
ChatID int64 `json:"chat_id"`
|
||||||
MessageThreadID int `json:"message_thread_id,omitempty"`
|
MessageThreadID int `json:"message_thread_id,omitempty"`
|
||||||
DirectMessagesTopicID int `json:"direct_messages_topic_id,omitempty"`
|
DirectMessagesTopicID int `json:"direct_messages_topic_id,omitempty"`
|
||||||
|
|
||||||
Emoji string `json:"emoji,omitempty"`
|
Emoji string `json:"emoji,omitempty"`
|
||||||
|
|
||||||
@@ -312,7 +400,16 @@ func (api *API) SendDice(params SendDiceP) (Message, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SendDiceWithContext is the context-aware variant of SendDice.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#senddice
|
||||||
|
func (api *API) SendDiceWithContext(ctx context.Context, params SendDiceP) (Message, error) {
|
||||||
|
req := NewRequestWithChatID[Message]("sendDice", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// SendMessageDraftP holds parameters for the sendMessageDraft method.
|
// SendMessageDraftP holds parameters for the sendMessageDraft method.
|
||||||
|
// See https://core.telegram.org/bots/api#sendmessagedraft
|
||||||
type SendMessageDraftP struct {
|
type SendMessageDraftP struct {
|
||||||
ChatID int64 `json:"chat_id"`
|
ChatID int64 `json:"chat_id"`
|
||||||
MessageThreadID int `json:"message_thread_id,omitempty"`
|
MessageThreadID int `json:"message_thread_id,omitempty"`
|
||||||
@@ -322,12 +419,22 @@ type SendMessageDraftP struct {
|
|||||||
Entities []MessageEntity `json:"entities,omitempty"`
|
Entities []MessageEntity `json:"entities,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendMessageDraft sends a previously saved draft message.
|
// SendMessageDraft sends or updates a draft message in the target chat.
|
||||||
|
// Returns True on success.
|
||||||
|
// See https://core.telegram.org/bots/api#sendmessagedraft
|
||||||
func (api *API) SendMessageDraft(params SendMessageDraftP) (bool, error) {
|
func (api *API) SendMessageDraft(params SendMessageDraftP) (bool, error) {
|
||||||
req := NewRequestWithChatID[bool]("sendMessageDraft", params, params.ChatID)
|
req := NewRequestWithChatID[bool]("sendMessageDraft", params, params.ChatID)
|
||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SendMessageDraftWithContext is the context-aware variant of SendMessageDraft.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#sendmessagedraft
|
||||||
|
func (api *API) SendMessageDraftWithContext(ctx context.Context, params SendMessageDraftP) (bool, error) {
|
||||||
|
req := NewRequestWithChatID[bool]("sendMessageDraft", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// SendChatActionP holds parameters for the sendChatAction method.
|
// SendChatActionP holds parameters for the sendChatAction method.
|
||||||
// See https://core.telegram.org/bots/api#sendchataction
|
// See https://core.telegram.org/bots/api#sendchataction
|
||||||
type SendChatActionP struct {
|
type SendChatActionP struct {
|
||||||
@@ -345,6 +452,14 @@ func (api *API) SendChatAction(params SendChatActionP) (bool, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SendChatActionWithContext is the context-aware variant of SendChatAction.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#sendchataction
|
||||||
|
func (api *API) SendChatActionWithContext(ctx context.Context, params SendChatActionP) (bool, error) {
|
||||||
|
req := NewRequestWithChatID[bool]("sendChatAction", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// SetMessageReactionP holds parameters for the setMessageReaction method.
|
// SetMessageReactionP holds parameters for the setMessageReaction method.
|
||||||
// See https://core.telegram.org/bots/api#setmessagereaction
|
// See https://core.telegram.org/bots/api#setmessagereaction
|
||||||
type SetMessageReactionP struct {
|
type SetMessageReactionP struct {
|
||||||
@@ -362,16 +477,26 @@ func (api *API) SetMessageReaction(params SetMessageReactionP) (bool, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetMessageReactionWithContext is the context-aware variant of SetMessageReaction.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#setmessagereaction
|
||||||
|
func (api *API) SetMessageReactionWithContext(ctx context.Context, params SetMessageReactionP) (bool, error) {
|
||||||
|
req := NewRequestWithChatID[bool]("setMessageReaction", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// EditMessageTextP holds parameters for the editMessageText method.
|
// EditMessageTextP holds parameters for the editMessageText method.
|
||||||
// See https://core.telegram.org/bots/api#editmessagetext
|
// See https://core.telegram.org/bots/api#editmessagetext
|
||||||
type EditMessageTextP struct {
|
type EditMessageTextP struct {
|
||||||
BusinessConnectionID string `json:"business_connection_id,omitempty"`
|
BusinessConnectionID string `json:"business_connection_id,omitempty"`
|
||||||
ChatID int64 `json:"chat_id,omitempty"`
|
ChatID int64 `json:"chat_id,omitempty"`
|
||||||
MessageID int `json:"message_id,omitempty"`
|
MessageID int `json:"message_id,omitempty"`
|
||||||
InlineMessageID string `json:"inline_message_id,omitempty"`
|
InlineMessageID string `json:"inline_message_id,omitempty"`
|
||||||
Text string `json:"text"`
|
Text string `json:"text"`
|
||||||
ParseMode ParseMode `json:"parse_mode,omitempty"`
|
ParseMode ParseMode `json:"parse_mode,omitempty"`
|
||||||
ReplyMarkup *ReplyMarkup `json:"reply_markup,omitempty"`
|
Entities []MessageEntity `json:"entities,omitempty"`
|
||||||
|
LinkPreviewOptions *LinkPreviewOptions `json:"link_preview_options,omitempty"`
|
||||||
|
ReplyMarkup *ReplyMarkup `json:"reply_markup,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// EditMessageText edits text messages.
|
// EditMessageText edits text messages.
|
||||||
@@ -390,16 +515,33 @@ func (api *API) EditMessageText(params EditMessageTextP) (Message, bool, error)
|
|||||||
return res, false, err
|
return res, false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EditMessageTextWithContext is the context-aware variant of EditMessageText.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#editmessagetext
|
||||||
|
func (api *API) EditMessageTextWithContext(ctx context.Context, params EditMessageTextP) (Message, bool, error) {
|
||||||
|
var zero Message
|
||||||
|
if params.InlineMessageID != "" {
|
||||||
|
req := NewRequestWithChatID[bool]("editMessageText", params, params.ChatID)
|
||||||
|
res, err := req.DoWithContext(ctx, api)
|
||||||
|
return zero, res, err
|
||||||
|
}
|
||||||
|
req := NewRequestWithChatID[Message]("editMessageText", params, params.ChatID)
|
||||||
|
res, err := req.DoWithContext(ctx, api)
|
||||||
|
return res, false, err
|
||||||
|
}
|
||||||
|
|
||||||
// EditMessageCaptionP holds parameters for the editMessageCaption method.
|
// EditMessageCaptionP holds parameters for the editMessageCaption method.
|
||||||
// See https://core.telegram.org/bots/api#editmessagecaption
|
// See https://core.telegram.org/bots/api#editmessagecaption
|
||||||
type EditMessageCaptionP struct {
|
type EditMessageCaptionP struct {
|
||||||
BusinessConnectionID string `json:"business_connection_id,omitempty"`
|
BusinessConnectionID string `json:"business_connection_id,omitempty"`
|
||||||
ChatID int64 `json:"chat_id,omitempty"`
|
ChatID int64 `json:"chat_id,omitempty"`
|
||||||
MessageID int `json:"message_id,omitempty"`
|
MessageID int `json:"message_id,omitempty"`
|
||||||
InlineMessageID string `json:"inline_message_id,omitempty"`
|
InlineMessageID string `json:"inline_message_id,omitempty"`
|
||||||
Caption string `json:"caption"`
|
Caption string `json:"caption"`
|
||||||
ParseMode ParseMode `json:"parse_mode,omitempty"`
|
ParseMode ParseMode `json:"parse_mode,omitempty"`
|
||||||
ReplyMarkup *ReplyMarkup `json:"reply_markup,omitempty"`
|
CaptionEntities []MessageEntity `json:"caption_entities,omitempty"`
|
||||||
|
ShowCaptionAboveMedia bool `json:"show_caption_above_media,omitempty"`
|
||||||
|
ReplyMarkup *ReplyMarkup `json:"reply_markup,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// EditMessageCaption edits captions of messages.
|
// EditMessageCaption edits captions of messages.
|
||||||
@@ -418,6 +560,21 @@ func (api *API) EditMessageCaption(params EditMessageCaptionP) (Message, bool, e
|
|||||||
return res, false, err
|
return res, false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EditMessageCaptionWithContext is the context-aware variant of EditMessageCaption.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#editmessagecaption
|
||||||
|
func (api *API) EditMessageCaptionWithContext(ctx context.Context, params EditMessageCaptionP) (Message, bool, error) {
|
||||||
|
var zero Message
|
||||||
|
if params.InlineMessageID != "" {
|
||||||
|
req := NewRequestWithChatID[bool]("editMessageCaption", params, params.ChatID)
|
||||||
|
res, err := req.DoWithContext(ctx, api)
|
||||||
|
return zero, res, err
|
||||||
|
}
|
||||||
|
req := NewRequestWithChatID[Message]("editMessageCaption", params, params.ChatID)
|
||||||
|
res, err := req.DoWithContext(ctx, api)
|
||||||
|
return res, false, err
|
||||||
|
}
|
||||||
|
|
||||||
// EditMessageMediaP holds parameters for the editMessageMedia method.
|
// EditMessageMediaP holds parameters for the editMessageMedia method.
|
||||||
// See https://core.telegram.org/bots/api#editmessagemedia
|
// See https://core.telegram.org/bots/api#editmessagemedia
|
||||||
type EditMessageMediaP struct {
|
type EditMessageMediaP struct {
|
||||||
@@ -425,7 +582,7 @@ type EditMessageMediaP struct {
|
|||||||
ChatID int64 `json:"chat_id,omitempty"`
|
ChatID int64 `json:"chat_id,omitempty"`
|
||||||
MessageID int `json:"message_id,omitempty"`
|
MessageID int `json:"message_id,omitempty"`
|
||||||
InlineMessageID string `json:"inline_message_id,omitempty"`
|
InlineMessageID string `json:"inline_message_id,omitempty"`
|
||||||
Message InputMedia `json:"message"`
|
Media InputMedia `json:"media"`
|
||||||
ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"`
|
ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -445,6 +602,21 @@ func (api *API) EditMessageMedia(params EditMessageMediaP) (Message, bool, error
|
|||||||
return res, false, err
|
return res, false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EditMessageMediaWithContext is the context-aware variant of EditMessageMedia.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#editmessagemedia
|
||||||
|
func (api *API) EditMessageMediaWithContext(ctx context.Context, params EditMessageMediaP) (Message, bool, error) {
|
||||||
|
var zero Message
|
||||||
|
if params.InlineMessageID != "" {
|
||||||
|
req := NewRequestWithChatID[bool]("editMessageMedia", params, params.ChatID)
|
||||||
|
res, err := req.DoWithContext(ctx, api)
|
||||||
|
return zero, res, err
|
||||||
|
}
|
||||||
|
req := NewRequestWithChatID[Message]("editMessageMedia", params, params.ChatID)
|
||||||
|
res, err := req.DoWithContext(ctx, api)
|
||||||
|
return res, false, err
|
||||||
|
}
|
||||||
|
|
||||||
// EditMessageLiveLocationP holds parameters for the editMessageLiveLocation method.
|
// EditMessageLiveLocationP holds parameters for the editMessageLiveLocation method.
|
||||||
// See https://core.telegram.org/bots/api#editmessagelivelocation
|
// See https://core.telegram.org/bots/api#editmessagelivelocation
|
||||||
type EditMessageLiveLocationP struct {
|
type EditMessageLiveLocationP struct {
|
||||||
@@ -478,6 +650,21 @@ func (api *API) EditMessageLiveLocation(params EditMessageLiveLocationP) (Messag
|
|||||||
return res, false, err
|
return res, false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EditMessageLiveLocationWithContext is the context-aware variant of EditMessageLiveLocation.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#editmessagelivelocation
|
||||||
|
func (api *API) EditMessageLiveLocationWithContext(ctx context.Context, params EditMessageLiveLocationP) (Message, bool, error) {
|
||||||
|
var zero Message
|
||||||
|
if params.InlineMessageID != "" {
|
||||||
|
req := NewRequestWithChatID[bool]("editMessageLiveLocation", params, params.ChatID)
|
||||||
|
res, err := req.DoWithContext(ctx, api)
|
||||||
|
return zero, res, err
|
||||||
|
}
|
||||||
|
req := NewRequestWithChatID[Message]("editMessageLiveLocation", params, params.ChatID)
|
||||||
|
res, err := req.DoWithContext(ctx, api)
|
||||||
|
return res, false, err
|
||||||
|
}
|
||||||
|
|
||||||
// StopMessageLiveLocationP holds parameters for the stopMessageLiveLocation method.
|
// StopMessageLiveLocationP holds parameters for the stopMessageLiveLocation method.
|
||||||
// See https://core.telegram.org/bots/api#stopmessagelivelocation
|
// See https://core.telegram.org/bots/api#stopmessagelivelocation
|
||||||
type StopMessageLiveLocationP struct {
|
type StopMessageLiveLocationP struct {
|
||||||
@@ -504,6 +691,21 @@ func (api *API) StopMessageLiveLocation(params StopMessageLiveLocationP) (Messag
|
|||||||
return res, false, err
|
return res, false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StopMessageLiveLocationWithContext is the context-aware variant of StopMessageLiveLocation.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#stopmessagelivelocation
|
||||||
|
func (api *API) StopMessageLiveLocationWithContext(ctx context.Context, params StopMessageLiveLocationP) (Message, bool, error) {
|
||||||
|
var zero Message
|
||||||
|
if params.InlineMessageID != "" {
|
||||||
|
req := NewRequestWithChatID[bool]("stopMessageLiveLocation", params, params.ChatID)
|
||||||
|
res, err := req.DoWithContext(ctx, api)
|
||||||
|
return zero, res, err
|
||||||
|
}
|
||||||
|
req := NewRequestWithChatID[Message]("stopMessageLiveLocation", params, params.ChatID)
|
||||||
|
res, err := req.DoWithContext(ctx, api)
|
||||||
|
return res, false, err
|
||||||
|
}
|
||||||
|
|
||||||
// EditMessageChecklistP holds parameters for the editMessageChecklist method.
|
// EditMessageChecklistP holds parameters for the editMessageChecklist method.
|
||||||
type EditMessageChecklistP struct {
|
type EditMessageChecklistP struct {
|
||||||
BusinessConnectionID string `json:"business_connection_id"`
|
BusinessConnectionID string `json:"business_connection_id"`
|
||||||
@@ -520,6 +722,14 @@ func (api *API) EditMessageChecklist(params EditMessageChecklistP) (Message, err
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EditMessageChecklistWithContext is the context-aware variant of EditMessageChecklist.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#editmessagechecklist
|
||||||
|
func (api *API) EditMessageChecklistWithContext(ctx context.Context, params EditMessageChecklistP) (Message, error) {
|
||||||
|
req := NewRequestWithChatID[Message]("editMessageChecklist", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// EditMessageReplyMarkupP holds parameters for the editMessageReplyMarkup method.
|
// EditMessageReplyMarkupP holds parameters for the editMessageReplyMarkup method.
|
||||||
// See https://core.telegram.org/bots/api#editmessagereplymarkup
|
// See https://core.telegram.org/bots/api#editmessagereplymarkup
|
||||||
type EditMessageReplyMarkupP struct {
|
type EditMessageReplyMarkupP struct {
|
||||||
@@ -546,13 +756,28 @@ func (api *API) EditMessageReplyMarkup(params EditMessageReplyMarkupP) (Message,
|
|||||||
return res, false, err
|
return res, false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EditMessageReplyMarkupWithContext is the context-aware variant of EditMessageReplyMarkup.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#editmessagereplymarkup
|
||||||
|
func (api *API) EditMessageReplyMarkupWithContext(ctx context.Context, params EditMessageReplyMarkupP) (Message, bool, error) {
|
||||||
|
var zero Message
|
||||||
|
if params.InlineMessageID != "" {
|
||||||
|
req := NewRequestWithChatID[bool]("editMessageReplyMarkup", params, params.ChatID)
|
||||||
|
res, err := req.DoWithContext(ctx, api)
|
||||||
|
return zero, res, err
|
||||||
|
}
|
||||||
|
req := NewRequestWithChatID[Message]("editMessageReplyMarkup", params, params.ChatID)
|
||||||
|
res, err := req.DoWithContext(ctx, api)
|
||||||
|
return res, false, err
|
||||||
|
}
|
||||||
|
|
||||||
// StopPollP holds parameters for the stopPoll method.
|
// StopPollP holds parameters for the stopPoll method.
|
||||||
// See https://core.telegram.org/bots/api#stoppoll
|
// See https://core.telegram.org/bots/api#stoppoll
|
||||||
type StopPollP struct {
|
type StopPollP struct {
|
||||||
BusinessConnectionID string `json:"business_connection_id,omitempty"`
|
BusinessConnectionID string `json:"business_connection_id,omitempty"`
|
||||||
ChatID int64 `json:"chat_id"`
|
ChatID int64 `json:"chat_id"`
|
||||||
MessageID int `json:"message_id"`
|
MessageID int `json:"message_id"`
|
||||||
InlineMessageID string `json:"inline_message_id,omitempty"`
|
ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// StopPoll stops a poll that was sent by the bot.
|
// StopPoll stops a poll that was sent by the bot.
|
||||||
@@ -563,6 +788,14 @@ func (api *API) StopPoll(params StopPollP) (Poll, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StopPollWithContext is the context-aware variant of StopPoll.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#stoppoll
|
||||||
|
func (api *API) StopPollWithContext(ctx context.Context, params StopPollP) (Poll, error) {
|
||||||
|
req := NewRequestWithChatID[Poll]("stopPoll", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// ApproveSuggestedPostP holds parameters for the approveSuggestedPost method.
|
// ApproveSuggestedPostP holds parameters for the approveSuggestedPost method.
|
||||||
// See https://core.telegram.org/bots/api#approvesuggestedpost
|
// See https://core.telegram.org/bots/api#approvesuggestedpost
|
||||||
type ApproveSuggestedPostP struct {
|
type ApproveSuggestedPostP struct {
|
||||||
@@ -579,6 +812,14 @@ func (api *API) ApproveSuggestedPost(params ApproveSuggestedPostP) (bool, error)
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ApproveSuggestedPostWithContext is the context-aware variant of ApproveSuggestedPost.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#approvesuggestedpost
|
||||||
|
func (api *API) ApproveSuggestedPostWithContext(ctx context.Context, params ApproveSuggestedPostP) (bool, error) {
|
||||||
|
req := NewRequestWithChatID[bool]("approveSuggestedPost", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// DeclineSuggestedPostP holds parameters for the declineSuggestedPost method.
|
// DeclineSuggestedPostP holds parameters for the declineSuggestedPost method.
|
||||||
// See https://core.telegram.org/bots/api#declinesuggestedpost
|
// See https://core.telegram.org/bots/api#declinesuggestedpost
|
||||||
type DeclineSuggestedPostP struct {
|
type DeclineSuggestedPostP struct {
|
||||||
@@ -595,6 +836,14 @@ func (api *API) DeclineSuggestedPost(params DeclineSuggestedPostP) (bool, error)
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeclineSuggestedPostWithContext is the context-aware variant of DeclineSuggestedPost.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#declinesuggestedpost
|
||||||
|
func (api *API) DeclineSuggestedPostWithContext(ctx context.Context, params DeclineSuggestedPostP) (bool, error) {
|
||||||
|
req := NewRequestWithChatID[bool]("declineSuggestedPost", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// DeleteMessageP holds parameters for the deleteMessage method.
|
// DeleteMessageP holds parameters for the deleteMessage method.
|
||||||
// See https://core.telegram.org/bots/api#deletemessage
|
// See https://core.telegram.org/bots/api#deletemessage
|
||||||
type DeleteMessageP struct {
|
type DeleteMessageP struct {
|
||||||
@@ -610,6 +859,14 @@ func (api *API) DeleteMessage(params DeleteMessageP) (bool, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteMessageWithContext is the context-aware variant of DeleteMessage.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#deletemessage
|
||||||
|
func (api *API) DeleteMessageWithContext(ctx context.Context, params DeleteMessageP) (bool, error) {
|
||||||
|
req := NewRequestWithChatID[bool]("deleteMessage", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// DeleteMessagesP holds parameters for the deleteMessages method.
|
// DeleteMessagesP holds parameters for the deleteMessages method.
|
||||||
// See https://core.telegram.org/bots/api#deletemessages
|
// See https://core.telegram.org/bots/api#deletemessages
|
||||||
type DeleteMessagesP struct {
|
type DeleteMessagesP struct {
|
||||||
@@ -625,6 +882,14 @@ func (api *API) DeleteMessages(params DeleteMessagesP) (bool, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteMessagesWithContext is the context-aware variant of DeleteMessages.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#deletemessages
|
||||||
|
func (api *API) DeleteMessagesWithContext(ctx context.Context, params DeleteMessagesP) (bool, error) {
|
||||||
|
req := NewRequestWithChatID[bool]("deleteMessages", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// AnswerCallbackQueryP holds parameters for the answerCallbackQuery method.
|
// AnswerCallbackQueryP holds parameters for the answerCallbackQuery method.
|
||||||
// See https://core.telegram.org/bots/api#answercallbackquery
|
// See https://core.telegram.org/bots/api#answercallbackquery
|
||||||
type AnswerCallbackQueryP struct {
|
type AnswerCallbackQueryP struct {
|
||||||
@@ -642,3 +907,11 @@ func (api *API) AnswerCallbackQuery(params AnswerCallbackQueryP) (bool, error) {
|
|||||||
req := NewRequest[bool]("answerCallbackQuery", params)
|
req := NewRequest[bool]("answerCallbackQuery", params)
|
||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AnswerCallbackQueryWithContext is the context-aware variant of AnswerCallbackQuery.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#answercallbackquery
|
||||||
|
func (api *API) AnswerCallbackQueryWithContext(ctx context.Context, params AnswerCallbackQueryP) (bool, error) {
|
||||||
|
req := NewRequest[bool]("answerCallbackQuery", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|||||||
+82
-15
@@ -2,6 +2,11 @@ package tgapi
|
|||||||
|
|
||||||
import "git.nix13.pw/scuroneko/extypes"
|
import "git.nix13.pw/scuroneko/extypes"
|
||||||
|
|
||||||
|
// MessageID represents a message identifier wrapper returned by some API methods.
|
||||||
|
type MessageID struct {
|
||||||
|
MessageID int `json:"message_id"`
|
||||||
|
}
|
||||||
|
|
||||||
// MessageReplyMarkup represents an inline keyboard markup for a message.
|
// MessageReplyMarkup represents an inline keyboard markup for a message.
|
||||||
// It is used in the Message type.
|
// It is used in the Message type.
|
||||||
type MessageReplyMarkup struct {
|
type MessageReplyMarkup struct {
|
||||||
@@ -113,8 +118,8 @@ type MessageEntity struct {
|
|||||||
// ReplyParameters describes the parameters to use when replying to a message.
|
// ReplyParameters describes the parameters to use when replying to a message.
|
||||||
// See https://core.telegram.org/bots/api#replyparameters
|
// See https://core.telegram.org/bots/api#replyparameters
|
||||||
type ReplyParameters struct {
|
type ReplyParameters struct {
|
||||||
MessageID int `json:"message_id"`
|
MessageID int `json:"message_id"`
|
||||||
ChatID int `json:"chat_id,omitempty"`
|
ChatID int64 `json:"chat_id,omitempty"`
|
||||||
|
|
||||||
AllowSendingWithoutReply bool `json:"allow_sending_without_reply,omitempty"`
|
AllowSendingWithoutReply bool `json:"allow_sending_without_reply,omitempty"`
|
||||||
Quote string `json:"quote,omitempty"`
|
Quote string `json:"quote,omitempty"`
|
||||||
@@ -139,12 +144,12 @@ type LinkPreviewOptions struct {
|
|||||||
type ReplyMarkup struct {
|
type ReplyMarkup struct {
|
||||||
InlineKeyboard [][]InlineKeyboardButton `json:"inline_keyboard,omitempty"`
|
InlineKeyboard [][]InlineKeyboardButton `json:"inline_keyboard,omitempty"`
|
||||||
|
|
||||||
Keyboard [][]int `json:"keyboard,omitempty"`
|
Keyboard [][]KeyboardButton `json:"keyboard,omitempty"`
|
||||||
IsPersistent bool `json:"is_persistent,omitempty"`
|
IsPersistent bool `json:"is_persistent,omitempty"`
|
||||||
ResizeKeyboard bool `json:"resize_keyboard,omitempty"`
|
ResizeKeyboard bool `json:"resize_keyboard,omitempty"`
|
||||||
OneTimeKeyboard bool `json:"one_time_keyboard,omitempty"`
|
OneTimeKeyboard bool `json:"one_time_keyboard,omitempty"`
|
||||||
InputFieldPlaceholder string `json:"input_field_placeholder,omitempty"`
|
InputFieldPlaceholder string `json:"input_field_placeholder,omitempty"`
|
||||||
Selective bool `json:"selective,omitempty"`
|
Selective bool `json:"selective,omitempty"`
|
||||||
|
|
||||||
RemoveKeyboard bool `json:"remove_keyboard,omitempty"`
|
RemoveKeyboard bool `json:"remove_keyboard,omitempty"`
|
||||||
|
|
||||||
@@ -160,6 +165,60 @@ type InlineKeyboardMarkup struct {
|
|||||||
// KeyboardButtonStyle represents the style of a keyboard button.
|
// KeyboardButtonStyle represents the style of a keyboard button.
|
||||||
type KeyboardButtonStyle string
|
type KeyboardButtonStyle string
|
||||||
|
|
||||||
|
const (
|
||||||
|
KeyboardButtonStyleDanger KeyboardButtonStyle = "danger"
|
||||||
|
KeyboardButtonStyleSuccess KeyboardButtonStyle = "success"
|
||||||
|
KeyboardButtonStylePrimary KeyboardButtonStyle = "primary"
|
||||||
|
)
|
||||||
|
|
||||||
|
// KeyboardButton represents one button of the reply keyboard.
|
||||||
|
// See https://core.telegram.org/bots/api#keyboardbutton
|
||||||
|
type KeyboardButton struct {
|
||||||
|
Text string `json:"text"`
|
||||||
|
IconCustomEmojiID string `json:"icon_custom_emoji_id,omitempty"`
|
||||||
|
Style KeyboardButtonStyle `json:"style,omitempty"`
|
||||||
|
RequestUsers *KeyboardButtonRequestUsers `json:"request_users,omitempty"`
|
||||||
|
RequestChat *KeyboardButtonRequestChat `json:"request_chat,omitempty"`
|
||||||
|
RequestContact bool `json:"request_contact,omitempty"`
|
||||||
|
RequestLocation bool `json:"request_location,omitempty"`
|
||||||
|
RequestPoll *KeyboardButtonPollType `json:"request_poll,omitempty"`
|
||||||
|
WebApp *WebAppInfo `json:"web_app,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyboardButtonRequestUsers defines criteria used to request suitable users.
|
||||||
|
// See https://core.telegram.org/bots/api#keyboardbuttonrequestusers
|
||||||
|
type KeyboardButtonRequestUsers struct {
|
||||||
|
RequestID int `json:"request_id"`
|
||||||
|
UserIsBot *bool `json:"user_is_bot,omitempty"`
|
||||||
|
UserIsPremium *bool `json:"user_is_premium,omitempty"`
|
||||||
|
MaxQuantity int `json:"max_quantity,omitempty"`
|
||||||
|
RequestName bool `json:"request_name,omitempty"`
|
||||||
|
RequestUsername bool `json:"request_username,omitempty"`
|
||||||
|
RequestPhoto bool `json:"request_photo,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyboardButtonRequestChat defines criteria used to request a suitable chat.
|
||||||
|
// See https://core.telegram.org/bots/api#keyboardbuttonrequestchat
|
||||||
|
type KeyboardButtonRequestChat struct {
|
||||||
|
RequestID int `json:"request_id"`
|
||||||
|
ChatIsChannel bool `json:"chat_is_channel"`
|
||||||
|
ChatIsForum *bool `json:"chat_is_forum,omitempty"`
|
||||||
|
ChatHasUsername *bool `json:"chat_has_username,omitempty"`
|
||||||
|
ChatIsCreated *bool `json:"chat_is_created,omitempty"`
|
||||||
|
UserAdministratorRights *ChatAdministratorRights `json:"user_administrator_rights,omitempty"`
|
||||||
|
BotAdministratorRights *ChatAdministratorRights `json:"bot_administrator_rights,omitempty"`
|
||||||
|
BotIsMember bool `json:"bot_is_member,omitempty"`
|
||||||
|
RequestTitle bool `json:"request_title,omitempty"`
|
||||||
|
RequestUsername bool `json:"request_username,omitempty"`
|
||||||
|
RequestPhoto bool `json:"request_photo,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyboardButtonPollType represents the type of a poll that may be created from a keyboard button.
|
||||||
|
// See https://core.telegram.org/bots/api#keyboardbuttonpolltype
|
||||||
|
type KeyboardButtonPollType struct {
|
||||||
|
Type PollType `json:"type,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
// InlineKeyboardButton represents one button of an inline keyboard.
|
// InlineKeyboardButton represents one button of an inline keyboard.
|
||||||
// See https://core.telegram.org/bots/api#inlinekeyboardbutton
|
// See https://core.telegram.org/bots/api#inlinekeyboardbutton
|
||||||
type InlineKeyboardButton struct {
|
type InlineKeyboardButton struct {
|
||||||
@@ -173,17 +232,24 @@ type InlineKeyboardButton struct {
|
|||||||
// ReplyKeyboardMarkup represents a custom keyboard with reply options.
|
// ReplyKeyboardMarkup represents a custom keyboard with reply options.
|
||||||
// See https://core.telegram.org/bots/api#replykeyboardmarkup
|
// See https://core.telegram.org/bots/api#replykeyboardmarkup
|
||||||
type ReplyKeyboardMarkup struct {
|
type ReplyKeyboardMarkup struct {
|
||||||
Keyboard [][]int `json:"keyboard"`
|
Keyboard [][]KeyboardButton `json:"keyboard"`
|
||||||
|
IsPersistent bool `json:"is_persistent,omitempty"`
|
||||||
|
ResizeKeyboard bool `json:"resize_keyboard,omitempty"`
|
||||||
|
OneTimeKeyboard bool `json:"one_time_keyboard,omitempty"`
|
||||||
|
InputFieldPlaceholder string `json:"input_field_placeholder,omitempty"`
|
||||||
|
Selective bool `json:"selective,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// CallbackQuery represents an incoming callback query from a callback button in an inline keyboard.
|
// CallbackQuery represents an incoming callback query from a callback button in an inline keyboard.
|
||||||
// See https://core.telegram.org/bots/api#callbackquery
|
// See https://core.telegram.org/bots/api#callbackquery
|
||||||
type CallbackQuery struct {
|
type CallbackQuery struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
From User `json:"from"`
|
From User `json:"from"`
|
||||||
Message Message `json:"message"`
|
Message *Message `json:"message,omitempty"`
|
||||||
|
InlineMessageID *string `json:"inline_message_id,omitempty"`
|
||||||
Data string `json:"data"`
|
ChatInstance string `json:"chat_instance,omitempty"`
|
||||||
|
Data string `json:"data,omitempty"`
|
||||||
|
GameShortName string `json:"game_short_name,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// InputPollOption contains information about one answer option in a poll to be sent.
|
// InputPollOption contains information about one answer option in a poll to be sent.
|
||||||
@@ -231,7 +297,8 @@ const (
|
|||||||
ChatActionUploadDocument ChatActionType = "upload_document"
|
ChatActionUploadDocument ChatActionType = "upload_document"
|
||||||
ChatActionChooseSticker ChatActionType = "choose_sticker"
|
ChatActionChooseSticker ChatActionType = "choose_sticker"
|
||||||
ChatActionFindLocation ChatActionType = "find_location"
|
ChatActionFindLocation ChatActionType = "find_location"
|
||||||
ChatActionUploadVideoNone ChatActionType = "upload_video_none"
|
ChatActionUploadVideoNote ChatActionType = "upload_video_note"
|
||||||
|
ChatActionUploadVideoNone ChatActionType = ChatActionUploadVideoNote
|
||||||
)
|
)
|
||||||
|
|
||||||
// MessageReactionUpdated represents a change of a reaction on a message.
|
// MessageReactionUpdated represents a change of a reaction on a message.
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package tgapi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestReplyKeyboardMarkupMarshalsKeyboardButtons(t *testing.T) {
|
||||||
|
markup := ReplyKeyboardMarkup{
|
||||||
|
Keyboard: [][]KeyboardButton{{
|
||||||
|
{
|
||||||
|
Text: "Create poll",
|
||||||
|
RequestPoll: &KeyboardButtonPollType{Type: PollTypeQuiz},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := json.Marshal(markup)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Marshal returned error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
got := string(data)
|
||||||
|
if !strings.Contains(got, `"keyboard":[[{"text":"Create poll","request_poll":{"type":"quiz"}}]]`) {
|
||||||
|
t.Fatalf("unexpected reply keyboard JSON: %s", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestChatActionUploadVideoNoteValue(t *testing.T) {
|
||||||
|
if ChatActionUploadVideoNote != "upload_video_note" {
|
||||||
|
t.Fatalf("unexpected chat action value: %q", ChatActionUploadVideoNote)
|
||||||
|
}
|
||||||
|
if ChatActionUploadVideoNone != ChatActionUploadVideoNote {
|
||||||
|
t.Fatalf("expected deprecated alias to match upload_video_note, got %q", ChatActionUploadVideoNone)
|
||||||
|
}
|
||||||
|
}
|
||||||
+142
-23
@@ -1,38 +1,21 @@
|
|||||||
package tgapi
|
package tgapi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"git.nix13.pw/scuroneko/laniakea/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ParseMode represents the text formatting mode for message parsing.
|
|
||||||
type ParseMode string
|
|
||||||
|
|
||||||
const (
|
|
||||||
// ParseMDV2 enables MarkdownV2 style parsing.
|
|
||||||
ParseMDV2 ParseMode = "MarkdownV2"
|
|
||||||
// ParseHTML enables HTML style parsing.
|
|
||||||
ParseHTML ParseMode = "HTML"
|
|
||||||
// ParseMD enables legacy Markdown style parsing.
|
|
||||||
ParseMD ParseMode = "Markdown"
|
|
||||||
// ParseNone disables any parsing.
|
|
||||||
ParseNone ParseMode = "None"
|
|
||||||
)
|
|
||||||
|
|
||||||
// EmptyParams is a placeholder for methods that take no parameters.
|
|
||||||
type EmptyParams struct{}
|
|
||||||
|
|
||||||
// NoParams is a convenient instance of EmptyParams.
|
|
||||||
var NoParams = EmptyParams{}
|
|
||||||
|
|
||||||
// UpdateParams holds parameters for the getUpdates method.
|
// UpdateParams holds parameters for the getUpdates method.
|
||||||
// See https://core.telegram.org/bots/api#getupdates
|
// See https://core.telegram.org/bots/api#getupdates
|
||||||
type UpdateParams struct {
|
type UpdateParams struct {
|
||||||
Offset *int `json:"offset,omitempty"`
|
Offset *int `json:"offset,omitempty"`
|
||||||
Limit *int `json:"limit,omitempty"`
|
Limit *int `json:"limit,omitempty"`
|
||||||
Timeout *int `json:"timeout,omitempty"`
|
Timeout *int `json:"timeout,omitempty"`
|
||||||
AllowedUpdates []UpdateType `json:"allowed_updates"`
|
AllowedUpdates []UpdateType `json:"allowed_updates,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetMe returns basic information about the bot.
|
// GetMe returns basic information about the bot.
|
||||||
@@ -42,6 +25,14 @@ func (api *API) GetMe() (User, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetMeWithContext is the context-aware variant of GetMe.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#getme
|
||||||
|
func (api *API) GetMeWithContext(ctx context.Context) (User, error) {
|
||||||
|
req := NewRequest[User, EmptyParams]("getMe", NoParams)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// LogOut logs the bot out from the cloud Bot API server.
|
// LogOut logs the bot out from the cloud Bot API server.
|
||||||
// Returns true on success.
|
// Returns true on success.
|
||||||
// See https://core.telegram.org/bots/api#logout
|
// See https://core.telegram.org/bots/api#logout
|
||||||
@@ -50,6 +41,14 @@ func (api *API) LogOut() (bool, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LogOutWithContext is the context-aware variant of LogOut.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#logout
|
||||||
|
func (api *API) LogOutWithContext(ctx context.Context) (bool, error) {
|
||||||
|
req := NewRequest[bool, EmptyParams]("logOut", NoParams)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// Close closes the bot instance on the local server.
|
// Close closes the bot instance on the local server.
|
||||||
// Returns true on success.
|
// Returns true on success.
|
||||||
// See https://core.telegram.org/bots/api#close
|
// See https://core.telegram.org/bots/api#close
|
||||||
@@ -58,6 +57,14 @@ func (api *API) Close() (bool, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CloseWithContext is the context-aware variant of Close.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#close
|
||||||
|
func (api *API) CloseWithContext(ctx context.Context) (bool, error) {
|
||||||
|
req := NewRequest[bool, EmptyParams]("close", NoParams)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// GetUpdates receives incoming updates using long polling.
|
// GetUpdates receives incoming updates using long polling.
|
||||||
// See https://core.telegram.org/bots/api#getupdates
|
// See https://core.telegram.org/bots/api#getupdates
|
||||||
func (api *API) GetUpdates(params UpdateParams) ([]Update, error) {
|
func (api *API) GetUpdates(params UpdateParams) ([]Update, error) {
|
||||||
@@ -65,6 +72,81 @@ func (api *API) GetUpdates(params UpdateParams) ([]Update, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetUpdatesWithContext is the context-aware variant of GetUpdates.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#getupdates
|
||||||
|
func (api *API) GetUpdatesWithContext(ctx context.Context, params UpdateParams) ([]Update, error) {
|
||||||
|
req := NewRequest[[]Update]("getUpdates", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetWebhookP holds parameters for the setWebhook method.
|
||||||
|
// To upload a self-signed certificate, use Uploader.SetWebhook.
|
||||||
|
// See https://core.telegram.org/bots/api#setwebhook
|
||||||
|
type SetWebhookP struct {
|
||||||
|
URL string `json:"url"`
|
||||||
|
IPAddress string `json:"ip_address,omitempty"`
|
||||||
|
MaxConnections int `json:"max_connections,omitempty"`
|
||||||
|
AllowedUpdates []UpdateType `json:"allowed_updates,omitempty"`
|
||||||
|
DropPendingUpdates bool `json:"drop_pending_updates,omitempty"`
|
||||||
|
SecretToken string `json:"secret_token,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetWebhook sets a webhook URL for incoming updates.
|
||||||
|
// For certificate upload, use Uploader.SetWebhook.
|
||||||
|
// Returns true on success.
|
||||||
|
// See https://core.telegram.org/bots/api#setwebhook
|
||||||
|
func (api *API) SetWebhook(params SetWebhookP) (bool, error) {
|
||||||
|
req := NewRequest[bool]("setWebhook", params)
|
||||||
|
return req.Do(api)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetWebhookWithContext is the context-aware variant of SetWebhook.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// For certificate upload, use Uploader.SetWebhook.
|
||||||
|
// See https://core.telegram.org/bots/api#setwebhook
|
||||||
|
func (api *API) SetWebhookWithContext(ctx context.Context, params SetWebhookP) (bool, error) {
|
||||||
|
req := NewRequest[bool]("setWebhook", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteWebhookP holds parameters for the deleteWebhook method.
|
||||||
|
// See https://core.telegram.org/bots/api#deletewebhook
|
||||||
|
type DeleteWebhookP struct {
|
||||||
|
DropPendingUpdates bool `json:"drop_pending_updates,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteWebhook removes the current webhook integration.
|
||||||
|
// Returns true on success.
|
||||||
|
// See https://core.telegram.org/bots/api#deletewebhook
|
||||||
|
func (api *API) DeleteWebhook(params DeleteWebhookP) (bool, error) {
|
||||||
|
req := NewRequest[bool]("deleteWebhook", params)
|
||||||
|
return req.Do(api)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteWebhookWithContext is the context-aware variant of DeleteWebhook.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#deletewebhook
|
||||||
|
func (api *API) DeleteWebhookWithContext(ctx context.Context, params DeleteWebhookP) (bool, error) {
|
||||||
|
req := NewRequest[bool]("deleteWebhook", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetWebhookInfo returns the current webhook status.
|
||||||
|
// See https://core.telegram.org/bots/api#getwebhookinfo
|
||||||
|
func (api *API) GetWebhookInfo() (WebhookInfo, error) {
|
||||||
|
req := NewRequest[WebhookInfo]("getWebhookInfo", NoParams)
|
||||||
|
return req.Do(api)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetWebhookInfoWithContext is the context-aware variant of GetWebhookInfo.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#getwebhookinfo
|
||||||
|
func (api *API) GetWebhookInfoWithContext(ctx context.Context) (WebhookInfo, error) {
|
||||||
|
req := NewRequest[WebhookInfo]("getWebhookInfo", NoParams)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// GetFileP holds parameters for the getFile method.
|
// GetFileP holds parameters for the getFile method.
|
||||||
// See https://core.telegram.org/bots/api#getfile
|
// See https://core.telegram.org/bots/api#getfile
|
||||||
type GetFileP struct {
|
type GetFileP struct {
|
||||||
@@ -78,17 +160,54 @@ func (api *API) GetFile(params GetFileP) (File, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetFileWithContext is the context-aware variant of GetFile.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#getfile
|
||||||
|
func (api *API) GetFileWithContext(ctx context.Context, params GetFileP) (File, error) {
|
||||||
|
req := NewRequest[File]("getFile", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// GetFileByLink downloads a file from Telegram's file server using the provided file link.
|
// GetFileByLink downloads a file from Telegram's file server using the provided file link.
|
||||||
// The link is usually obtained from File.FilePath.
|
// The link is usually obtained from File.FilePath.
|
||||||
// See https://core.telegram.org/bots/api#file
|
// See https://core.telegram.org/bots/api#file
|
||||||
func (api *API) GetFileByLink(link string) ([]byte, error) {
|
func (api *API) GetFileByLink(link string) ([]byte, error) {
|
||||||
u := fmt.Sprintf("https://api.telegram.org/file/bot%s/%s", api.token, link)
|
return api.getFileByLink(context.Background(), link)
|
||||||
res, err := http.Get(u)
|
}
|
||||||
|
|
||||||
|
// GetFileByLinkWithContext is the context-aware variant of GetFileByLink.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#file
|
||||||
|
func (api *API) GetFileByLinkWithContext(ctx context.Context, link string) ([]byte, error) {
|
||||||
|
return api.getFileByLink(ctx, link)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *API) getFileByLink(ctx context.Context, link string) ([]byte, error) {
|
||||||
|
methodPrefix := ""
|
||||||
|
if api.useTestServer {
|
||||||
|
methodPrefix = "/test"
|
||||||
|
}
|
||||||
|
u := fmt.Sprintf("%s/file/bot%s%s/%s", api.apiUrl, api.token, methodPrefix, link)
|
||||||
|
|
||||||
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, u, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
req.Header.Set("User-Agent", fmt.Sprintf("Laniakea/%s", utils.VersionString))
|
||||||
|
|
||||||
|
res, err := api.client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
_ = res.Body.Close()
|
_ = res.Body.Close()
|
||||||
}()
|
}()
|
||||||
|
if res.StatusCode < http.StatusOK || res.StatusCode >= http.StatusMultipleChoices {
|
||||||
|
body, readErr := io.ReadAll(io.LimitReader(res.Body, 4<<10))
|
||||||
|
if readErr != nil {
|
||||||
|
return nil, fmt.Errorf("unexpected status %d", res.StatusCode)
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("unexpected status %d: %s", res.StatusCode, string(body))
|
||||||
|
}
|
||||||
return io.ReadAll(res.Body)
|
return io.ReadAll(res.Body)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,115 @@
|
|||||||
|
package tgapi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGetFileByLinkUsesConfiguredAPIURL(t *testing.T) {
|
||||||
|
var gotPath string
|
||||||
|
|
||||||
|
client := &http.Client{
|
||||||
|
Transport: roundTripFunc(func(req *http.Request) (*http.Response, error) {
|
||||||
|
gotPath = req.URL.Path
|
||||||
|
return &http.Response{
|
||||||
|
StatusCode: http.StatusOK,
|
||||||
|
Body: io.NopCloser(strings.NewReader("payload")),
|
||||||
|
}, nil
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
api := NewAPI(
|
||||||
|
NewAPIOpts("token").
|
||||||
|
SetAPIUrl("https://example.test").
|
||||||
|
SetHTTPClient(client),
|
||||||
|
)
|
||||||
|
defer func() {
|
||||||
|
if err := api.CloseApi(); err != nil {
|
||||||
|
t.Fatalf("CloseApi returned error: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
data, err := api.GetFileByLink("files/report.txt")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("GetFileByLink returned error: %v", err)
|
||||||
|
}
|
||||||
|
if string(data) != "payload" {
|
||||||
|
t.Fatalf("unexpected payload: %q", string(data))
|
||||||
|
}
|
||||||
|
if gotPath != "/file/bottoken/files/report.txt" {
|
||||||
|
t.Fatalf("unexpected request path: %s", gotPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetFileByLinkReturnsHTTPStatusError(t *testing.T) {
|
||||||
|
client := &http.Client{
|
||||||
|
Transport: roundTripFunc(func(req *http.Request) (*http.Response, error) {
|
||||||
|
return &http.Response{
|
||||||
|
StatusCode: http.StatusNotFound,
|
||||||
|
Body: io.NopCloser(strings.NewReader("missing\n")),
|
||||||
|
}, nil
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
api := NewAPI(
|
||||||
|
NewAPIOpts("token").
|
||||||
|
SetAPIUrl("https://example.test").
|
||||||
|
SetHTTPClient(client),
|
||||||
|
)
|
||||||
|
defer func() {
|
||||||
|
if err := api.CloseApi(); err != nil {
|
||||||
|
t.Fatalf("CloseApi returned error: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
_, err := api.GetFileByLink("files/report.txt")
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected error for non-2xx response")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetUpdatesOmitsAllowedUpdatesWhenEmpty(t *testing.T) {
|
||||||
|
var gotBody map[string]any
|
||||||
|
|
||||||
|
client := &http.Client{
|
||||||
|
Transport: roundTripFunc(func(req *http.Request) (*http.Response, error) {
|
||||||
|
body, err := io.ReadAll(req.Body)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to read request body: %v", err)
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(body, &gotBody); err != nil {
|
||||||
|
t.Fatalf("failed to decode request body: %v", err)
|
||||||
|
}
|
||||||
|
return &http.Response{
|
||||||
|
StatusCode: http.StatusOK,
|
||||||
|
Header: http.Header{"Content-Type": []string{"application/json"}},
|
||||||
|
Body: io.NopCloser(strings.NewReader(`{"ok":true,"result":[]}`)),
|
||||||
|
}, nil
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
api := NewAPI(
|
||||||
|
NewAPIOpts("token").
|
||||||
|
SetAPIUrl("https://example.test").
|
||||||
|
SetHTTPClient(client),
|
||||||
|
)
|
||||||
|
defer func() {
|
||||||
|
if err := api.CloseApi(); err != nil {
|
||||||
|
t.Fatalf("CloseApi returned error: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
updates, err := api.GetUpdates(UpdateParams{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("GetUpdates returned error: %v", err)
|
||||||
|
}
|
||||||
|
if len(updates) != 0 {
|
||||||
|
t.Fatalf("expected no updates, got %d", len(updates))
|
||||||
|
}
|
||||||
|
if _, exists := gotBody["allowed_updates"]; exists {
|
||||||
|
t.Fatalf("expected allowed_updates to be omitted, got %v", gotBody["allowed_updates"])
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package tgapi
|
||||||
|
|
||||||
|
// ParseMode represents the text formatting mode for message parsing.
|
||||||
|
type ParseMode string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// ParseMDV2 enables MarkdownV2 style parsing.
|
||||||
|
ParseMDV2 ParseMode = "MarkdownV2"
|
||||||
|
// ParseHTML enables HTML style parsing.
|
||||||
|
ParseHTML ParseMode = "HTML"
|
||||||
|
// ParseMD enables legacy Markdown style parsing.
|
||||||
|
ParseMD ParseMode = "Markdown"
|
||||||
|
// ParseNone disables any parsing.
|
||||||
|
ParseNone ParseMode = "None"
|
||||||
|
)
|
||||||
|
|
||||||
|
// EmptyParams is a placeholder for methods that take no parameters.
|
||||||
|
type EmptyParams struct{}
|
||||||
|
|
||||||
|
// NoParams is a convenient instance of EmptyParams.
|
||||||
|
var NoParams = EmptyParams{}
|
||||||
|
|
||||||
|
// WebhookInfo describes the current webhook status.
|
||||||
|
// See https://core.telegram.org/bots/api#webhookinfo
|
||||||
|
type WebhookInfo struct {
|
||||||
|
URL string `json:"url"`
|
||||||
|
HasCustomCertificate bool `json:"has_custom_certificate"`
|
||||||
|
PendingUpdateCount int `json:"pending_update_count"`
|
||||||
|
IPAddress string `json:"ip_address,omitempty"`
|
||||||
|
LastErrorDate int `json:"last_error_date,omitempty"`
|
||||||
|
LastErrorMessage string `json:"last_error_message,omitempty"`
|
||||||
|
LastSynchronizationErrorDate int `json:"last_synchronization_error_date,omitempty"`
|
||||||
|
MaxConnections int `json:"max_connections,omitempty"`
|
||||||
|
AllowedUpdates []string `json:"allowed_updates,omitempty"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package tgapi
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
|
// SetPassportDataErrorsP holds parameters for the setPassportDataErrors method.
|
||||||
|
// See https://core.telegram.org/bots/api#setpassportdataerrors
|
||||||
|
type SetPassportDataErrorsP struct {
|
||||||
|
UserID int64 `json:"user_id"`
|
||||||
|
Errors []PassportElementError `json:"errors"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetPassportDataErrors informs a user about Telegram Passport data errors.
|
||||||
|
// Returns true on success.
|
||||||
|
// See https://core.telegram.org/bots/api#setpassportdataerrors
|
||||||
|
func (api *API) SetPassportDataErrors(params SetPassportDataErrorsP) (bool, error) {
|
||||||
|
req := NewRequest[bool]("setPassportDataErrors", params)
|
||||||
|
return req.Do(api)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetPassportDataErrorsWithContext is the context-aware variant of SetPassportDataErrors.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#setpassportdataerrors
|
||||||
|
func (api *API) SetPassportDataErrorsWithContext(ctx context.Context, params SetPassportDataErrorsP) (bool, error) {
|
||||||
|
req := NewRequest[bool]("setPassportDataErrors", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package tgapi
|
||||||
|
|
||||||
|
// PassportElementError is a JSON-serializable passport element error object.
|
||||||
|
// See https://core.telegram.org/bots/api#passportelementerror
|
||||||
|
type PassportElementError map[string]any
|
||||||
@@ -0,0 +1,150 @@
|
|||||||
|
package tgapi
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
|
// SendInvoiceP holds parameters for the sendInvoice method.
|
||||||
|
// See https://core.telegram.org/bots/api#sendinvoice
|
||||||
|
type SendInvoiceP struct {
|
||||||
|
ChatID int64 `json:"chat_id"`
|
||||||
|
MessageThreadID int `json:"message_thread_id,omitempty"`
|
||||||
|
DirectMessagesTopicID int `json:"direct_messages_topic_id,omitempty"`
|
||||||
|
|
||||||
|
Title string `json:"title"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Payload string `json:"payload"`
|
||||||
|
ProviderToken string `json:"provider_token,omitempty"`
|
||||||
|
Currency string `json:"currency"`
|
||||||
|
Prices []LabeledPrice `json:"prices"`
|
||||||
|
|
||||||
|
MaxTipAmount int `json:"max_tip_amount,omitempty"`
|
||||||
|
SuggestedTipAmounts []int `json:"suggested_tip_amounts,omitempty"`
|
||||||
|
StartParameter string `json:"start_parameter,omitempty"`
|
||||||
|
ProviderData string `json:"provider_data,omitempty"`
|
||||||
|
PhotoURL string `json:"photo_url,omitempty"`
|
||||||
|
PhotoSize int `json:"photo_size,omitempty"`
|
||||||
|
PhotoWidth int `json:"photo_width,omitempty"`
|
||||||
|
PhotoHeight int `json:"photo_height,omitempty"`
|
||||||
|
NeedName bool `json:"need_name,omitempty"`
|
||||||
|
NeedPhoneNumber bool `json:"need_phone_number,omitempty"`
|
||||||
|
NeedEmail bool `json:"need_email,omitempty"`
|
||||||
|
NeedShippingAddress bool `json:"need_shipping_address,omitempty"`
|
||||||
|
SendPhoneToProvider bool `json:"send_phone_number_to_provider,omitempty"`
|
||||||
|
SendEmailToProvider bool `json:"send_email_to_provider,omitempty"`
|
||||||
|
IsFlexible bool `json:"is_flexible,omitempty"`
|
||||||
|
DisableNotification bool `json:"disable_notification,omitempty"`
|
||||||
|
ProtectContent bool `json:"protect_content,omitempty"`
|
||||||
|
AllowPaidBroadcast bool `json:"allow_paid_broadcast,omitempty"`
|
||||||
|
MessageEffectID string `json:"message_effect_id,omitempty"`
|
||||||
|
|
||||||
|
SuggestedPostParameters *SuggestedPostParameters `json:"suggested_post_parameters,omitempty"`
|
||||||
|
ReplyParameters *ReplyParameters `json:"reply_parameters,omitempty"`
|
||||||
|
ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendInvoice sends an invoice.
|
||||||
|
// See https://core.telegram.org/bots/api#sendinvoice
|
||||||
|
func (api *API) SendInvoice(params SendInvoiceP) (Message, error) {
|
||||||
|
req := NewRequestWithChatID[Message]("sendInvoice", params, params.ChatID)
|
||||||
|
return req.Do(api)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendInvoiceWithContext is the context-aware variant of SendInvoice.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#sendinvoice
|
||||||
|
func (api *API) SendInvoiceWithContext(ctx context.Context, params SendInvoiceP) (Message, error) {
|
||||||
|
req := NewRequestWithChatID[Message]("sendInvoice", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateInvoiceLinkP holds parameters for the createInvoiceLink method.
|
||||||
|
// See https://core.telegram.org/bots/api#createinvoicelink
|
||||||
|
type CreateInvoiceLinkP struct {
|
||||||
|
BusinessConnectionID string `json:"business_connection_id,omitempty"`
|
||||||
|
|
||||||
|
Title string `json:"title"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Payload string `json:"payload"`
|
||||||
|
ProviderToken string `json:"provider_token,omitempty"`
|
||||||
|
Currency string `json:"currency"`
|
||||||
|
Prices []LabeledPrice `json:"prices"`
|
||||||
|
|
||||||
|
SubscriptionPeriod int `json:"subscription_period,omitempty"`
|
||||||
|
MaxTipAmount int `json:"max_tip_amount,omitempty"`
|
||||||
|
SuggestedTipAmounts []int `json:"suggested_tip_amounts,omitempty"`
|
||||||
|
ProviderData string `json:"provider_data,omitempty"`
|
||||||
|
PhotoURL string `json:"photo_url,omitempty"`
|
||||||
|
PhotoSize int `json:"photo_size,omitempty"`
|
||||||
|
PhotoWidth int `json:"photo_width,omitempty"`
|
||||||
|
PhotoHeight int `json:"photo_height,omitempty"`
|
||||||
|
NeedName bool `json:"need_name,omitempty"`
|
||||||
|
NeedPhoneNumber bool `json:"need_phone_number,omitempty"`
|
||||||
|
NeedEmail bool `json:"need_email,omitempty"`
|
||||||
|
NeedShippingAddress bool `json:"need_shipping_address,omitempty"`
|
||||||
|
SendPhoneToProvider bool `json:"send_phone_number_to_provider,omitempty"`
|
||||||
|
SendEmailToProvider bool `json:"send_email_to_provider,omitempty"`
|
||||||
|
IsFlexible bool `json:"is_flexible,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateInvoiceLink creates an invoice link.
|
||||||
|
// See https://core.telegram.org/bots/api#createinvoicelink
|
||||||
|
func (api *API) CreateInvoiceLink(params CreateInvoiceLinkP) (string, error) {
|
||||||
|
req := NewRequest[string]("createInvoiceLink", params)
|
||||||
|
return req.Do(api)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateInvoiceLinkWithContext is the context-aware variant of CreateInvoiceLink.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#createinvoicelink
|
||||||
|
func (api *API) CreateInvoiceLinkWithContext(ctx context.Context, params CreateInvoiceLinkP) (string, error) {
|
||||||
|
req := NewRequest[string]("createInvoiceLink", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AnswerShippingQueryP holds parameters for the answerShippingQuery method.
|
||||||
|
// See https://core.telegram.org/bots/api#answershippingquery
|
||||||
|
type AnswerShippingQueryP struct {
|
||||||
|
ShippingQueryID string `json:"shipping_query_id"`
|
||||||
|
OK bool `json:"ok"`
|
||||||
|
ShippingOptions []ShippingOption `json:"shipping_options,omitempty"`
|
||||||
|
ErrorMessage string `json:"error_message,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AnswerShippingQuery answers a shipping query.
|
||||||
|
// Returns true on success.
|
||||||
|
// See https://core.telegram.org/bots/api#answershippingquery
|
||||||
|
func (api *API) AnswerShippingQuery(params AnswerShippingQueryP) (bool, error) {
|
||||||
|
req := NewRequest[bool]("answerShippingQuery", params)
|
||||||
|
return req.Do(api)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AnswerShippingQueryWithContext is the context-aware variant of AnswerShippingQuery.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#answershippingquery
|
||||||
|
func (api *API) AnswerShippingQueryWithContext(ctx context.Context, params AnswerShippingQueryP) (bool, error) {
|
||||||
|
req := NewRequest[bool]("answerShippingQuery", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AnswerPreCheckoutQueryP holds parameters for the answerPreCheckoutQuery method.
|
||||||
|
// See https://core.telegram.org/bots/api#answerprecheckoutquery
|
||||||
|
type AnswerPreCheckoutQueryP struct {
|
||||||
|
PreCheckoutQueryID string `json:"pre_checkout_query_id"`
|
||||||
|
OK bool `json:"ok"`
|
||||||
|
ErrorMessage string `json:"error_message,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AnswerPreCheckoutQuery answers a pre-checkout query.
|
||||||
|
// Returns true on success.
|
||||||
|
// See https://core.telegram.org/bots/api#answerprecheckoutquery
|
||||||
|
func (api *API) AnswerPreCheckoutQuery(params AnswerPreCheckoutQueryP) (bool, error) {
|
||||||
|
req := NewRequest[bool]("answerPreCheckoutQuery", params)
|
||||||
|
return req.Do(api)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AnswerPreCheckoutQueryWithContext is the context-aware variant of AnswerPreCheckoutQuery.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#answerprecheckoutquery
|
||||||
|
func (api *API) AnswerPreCheckoutQueryWithContext(ctx context.Context, params AnswerPreCheckoutQueryP) (bool, error) {
|
||||||
|
req := NewRequest[bool]("answerPreCheckoutQuery", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package tgapi
|
||||||
|
|
||||||
|
// LabeledPrice represents a price portion.
|
||||||
|
// See https://core.telegram.org/bots/api#labeledprice
|
||||||
|
type LabeledPrice struct {
|
||||||
|
Label string `json:"label"`
|
||||||
|
Amount int `json:"amount"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ShippingOption represents one shipping option.
|
||||||
|
// See https://core.telegram.org/bots/api#shippingoption
|
||||||
|
type ShippingOption struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
Prices []LabeledPrice `json:"prices"`
|
||||||
|
}
|
||||||
+50
-17
@@ -14,13 +14,16 @@ type workerPool struct {
|
|||||||
workers int // количество воркеров (горутин)
|
workers int // количество воркеров (горутин)
|
||||||
wg sync.WaitGroup // синхронизирует завершение всех воркеров при остановке
|
wg sync.WaitGroup // синхронизирует завершение всех воркеров при остановке
|
||||||
quit chan struct{} // канал для сигнала остановки
|
quit chan struct{} // канал для сигнала остановки
|
||||||
|
stopOnce sync.Once // гарантирует идемпотентную остановку пула
|
||||||
started bool // флаг, указывающий, запущен ли пул
|
started bool // флаг, указывающий, запущен ли пул
|
||||||
|
stopped bool // флаг, указывающий, что пул остановлен
|
||||||
startedMu sync.Mutex // мьютекс для безопасного доступа к started
|
startedMu sync.Mutex // мьютекс для безопасного доступа к started
|
||||||
}
|
}
|
||||||
|
|
||||||
// requestEnvelope — приватная структура, инкапсулирующая задачу и канал для результата.
|
// requestEnvelope — приватная структура, инкапсулирующая задачу и канал для результата.
|
||||||
// Используется только внутри пакета для передачи задач воркерам.
|
// Используется только внутри пакета для передачи задач воркерам.
|
||||||
type requestEnvelope struct {
|
type requestEnvelope struct {
|
||||||
|
ctx context.Context // контекст конкретной задачи
|
||||||
doFunc func(context.Context) (any, error) // функция, выполняющая запрос
|
doFunc func(context.Context) (any, error) // функция, выполняющая запрос
|
||||||
resultCh chan requestResult // канал, через который воркер вернёт результат
|
resultCh chan requestResult // канал, через который воркер вернёт результат
|
||||||
}
|
}
|
||||||
@@ -53,7 +56,7 @@ func newWorkerPool(workers int, queueSize int) *workerPool {
|
|||||||
// start запускает воркеры (горутины), которые будут обрабатывать задачи из очереди.
|
// start запускает воркеры (горутины), которые будут обрабатывать задачи из очереди.
|
||||||
// Метод идемпотентен: если пул уже запущен — ничего не делает.
|
// Метод идемпотентен: если пул уже запущен — ничего не делает.
|
||||||
// Должен вызываться перед первым вызовом submit.
|
// Должен вызываться перед первым вызовом submit.
|
||||||
func (p *workerPool) start(ctx context.Context) {
|
func (p *workerPool) start() {
|
||||||
p.startedMu.Lock()
|
p.startedMu.Lock()
|
||||||
defer p.startedMu.Unlock()
|
defer p.startedMu.Unlock()
|
||||||
if p.started {
|
if p.started {
|
||||||
@@ -64,7 +67,7 @@ func (p *workerPool) start(ctx context.Context) {
|
|||||||
// Запускаем воркеры — каждый будет обрабатывать задачи в бесконечном цикле
|
// Запускаем воркеры — каждый будет обрабатывать задачи в бесконечном цикле
|
||||||
for i := 0; i < p.workers; i++ {
|
for i := 0; i < p.workers; i++ {
|
||||||
p.wg.Add(1)
|
p.wg.Add(1)
|
||||||
go p.worker(ctx) // запускаем горутину с контекстом
|
go p.worker() // запускаем горутину
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,8 +75,15 @@ func (p *workerPool) start(ctx context.Context) {
|
|||||||
// Отправляет сигнал остановки через quit-канал и ждёт завершения всех активных задач.
|
// Отправляет сигнал остановки через quit-канал и ждёт завершения всех активных задач.
|
||||||
// Безопасно вызывать многократно — после остановки повторные вызовы не имеют эффекта.
|
// Безопасно вызывать многократно — после остановки повторные вызовы не имеют эффекта.
|
||||||
func (p *workerPool) stop() {
|
func (p *workerPool) stop() {
|
||||||
close(p.quit) // сигнал для всех воркеров — выйти из цикла
|
p.stopOnce.Do(func() {
|
||||||
p.wg.Wait() // ждём, пока все воркеры завершатся
|
p.startedMu.Lock()
|
||||||
|
p.stopped = true
|
||||||
|
p.started = false
|
||||||
|
close(p.quit) // сигнал для всех воркеров — выйти из цикла
|
||||||
|
p.startedMu.Unlock()
|
||||||
|
|
||||||
|
p.wg.Wait() // ждём, пока все воркеры завершатся
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// submit отправляет задачу в очередь и возвращает канал, через который будет получен результат.
|
// submit отправляет задачу в очередь и возвращает канал, через который будет получен результат.
|
||||||
@@ -81,8 +91,15 @@ func (p *workerPool) stop() {
|
|||||||
// Канал результата имеет буфер 1, чтобы не блокировать воркера при записи.
|
// Канал результата имеет буфер 1, чтобы не блокировать воркера при записи.
|
||||||
// Контекст используется для отмены задачи, если клиент отменил запрос до отправки.
|
// Контекст используется для отмены задачи, если клиент отменил запрос до отправки.
|
||||||
func (p *workerPool) submit(ctx context.Context, do func(context.Context) (any, error)) (<-chan requestResult, error) {
|
func (p *workerPool) submit(ctx context.Context, do func(context.Context) (any, error)) (<-chan requestResult, error) {
|
||||||
|
p.startedMu.Lock()
|
||||||
|
if p.stopped || !p.started {
|
||||||
|
p.startedMu.Unlock()
|
||||||
|
return nil, ErrPoolStopped
|
||||||
|
}
|
||||||
|
|
||||||
// Проверяем, не превышена ли очередь
|
// Проверяем, не превышена ли очередь
|
||||||
if len(p.taskCh) >= p.queueSize {
|
if len(p.taskCh) >= p.queueSize {
|
||||||
|
p.startedMu.Unlock()
|
||||||
return nil, ErrPoolQueueFull
|
return nil, ErrPoolQueueFull
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,6 +108,7 @@ func (p *workerPool) submit(ctx context.Context, do func(context.Context) (any,
|
|||||||
|
|
||||||
// Создаём обёртку задачи
|
// Создаём обёртку задачи
|
||||||
envelope := requestEnvelope{
|
envelope := requestEnvelope{
|
||||||
|
ctx: ctx,
|
||||||
doFunc: do,
|
doFunc: do,
|
||||||
resultCh: resultCh,
|
resultCh: resultCh,
|
||||||
}
|
}
|
||||||
@@ -98,12 +116,15 @@ func (p *workerPool) submit(ctx context.Context, do func(context.Context) (any,
|
|||||||
// Пытаемся отправить задачу в очередь
|
// Пытаемся отправить задачу в очередь
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
|
p.startedMu.Unlock()
|
||||||
// Клиент отменил операцию до отправки — возвращаем ошибку отмены
|
// Клиент отменил операцию до отправки — возвращаем ошибку отмены
|
||||||
return nil, ctx.Err()
|
return nil, ctx.Err()
|
||||||
case p.taskCh <- envelope:
|
case p.taskCh <- envelope:
|
||||||
|
p.startedMu.Unlock()
|
||||||
// Успешно отправлено — возвращаем канал для чтения результата
|
// Успешно отправлено — возвращаем канал для чтения результата
|
||||||
return resultCh, nil
|
return resultCh, nil
|
||||||
default:
|
default:
|
||||||
|
p.startedMu.Unlock()
|
||||||
// Очередь переполнена — не должно происходить при проверке len(p.taskCh), но на всякий случай
|
// Очередь переполнена — не должно происходить при проверке len(p.taskCh), но на всякий случай
|
||||||
return nil, ErrPoolQueueFull
|
return nil, ErrPoolQueueFull
|
||||||
}
|
}
|
||||||
@@ -117,26 +138,38 @@ func (p *workerPool) submit(ctx context.Context, do func(context.Context) (any,
|
|||||||
// - закрывает канал, чтобы клиент мог прочитать и завершить
|
// - закрывает канал, чтобы клиент мог прочитать и завершить
|
||||||
//
|
//
|
||||||
// После закрытия quit-канала — воркер завершает работу.
|
// После закрытия quit-канала — воркер завершает работу.
|
||||||
func (p *workerPool) worker(ctx context.Context) {
|
func (p *workerPool) worker() {
|
||||||
defer p.wg.Done() // уменьшаем WaitGroup при завершении горутины
|
defer p.wg.Done() // уменьшаем WaitGroup при завершении горутины
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-p.quit:
|
case <-p.quit:
|
||||||
// Получен сигнал остановки — выходим из цикла
|
// Получен сигнал остановки — дренируем очередь и выходим.
|
||||||
return
|
// После stop() новые задачи не принимаются.
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case envelope := <-p.taskCh:
|
||||||
|
p.executeEnvelope(envelope)
|
||||||
|
default:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
case envelope := <-p.taskCh:
|
case envelope := <-p.taskCh:
|
||||||
// Выполняем задачу с переданным контекстом (клиентский или общий)
|
p.executeEnvelope(envelope)
|
||||||
value, err := envelope.doFunc(ctx)
|
|
||||||
|
|
||||||
// Записываем результат в канал — не блокируем, т.к. буфер 1
|
|
||||||
envelope.resultCh <- requestResult{
|
|
||||||
value: value,
|
|
||||||
err: err,
|
|
||||||
}
|
|
||||||
// Закрываем канал — клиент знает, что результат пришёл и больше не будет
|
|
||||||
close(envelope.resultCh)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *workerPool) executeEnvelope(envelope requestEnvelope) {
|
||||||
|
// Выполняем задачу с переданным контекстом (клиентский или общий)
|
||||||
|
value, err := envelope.doFunc(envelope.ctx)
|
||||||
|
|
||||||
|
// Записываем результат в канал — не блокируем, т.к. буфер 1
|
||||||
|
envelope.resultCh <- requestResult{
|
||||||
|
value: value,
|
||||||
|
err: err,
|
||||||
|
}
|
||||||
|
// Закрываем канал — клиент знает, что результат пришёл и больше не будет
|
||||||
|
close(envelope.resultCh)
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,87 @@
|
|||||||
|
package tgapi
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
|
// GetStarTransactionsP holds parameters for the getStarTransactions method.
|
||||||
|
// See https://core.telegram.org/bots/api#getstartransactions
|
||||||
|
type GetStarTransactionsP struct {
|
||||||
|
Offset int `json:"offset,omitempty"`
|
||||||
|
Limit int `json:"limit,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMyStarBalance returns the bot's Telegram Star balance.
|
||||||
|
// See https://core.telegram.org/bots/api#getmystarbalance
|
||||||
|
func (api *API) GetMyStarBalance() (StarAmount, error) {
|
||||||
|
req := NewRequest[StarAmount]("getMyStarBalance", NoParams)
|
||||||
|
return req.Do(api)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMyStarBalanceWithContext is the context-aware variant of GetMyStarBalance.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#getmystarbalance
|
||||||
|
func (api *API) GetMyStarBalanceWithContext(ctx context.Context) (StarAmount, error) {
|
||||||
|
req := NewRequest[StarAmount]("getMyStarBalance", NoParams)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetStarTransactions returns Telegram Star transactions for the bot.
|
||||||
|
// See https://core.telegram.org/bots/api#getstartransactions
|
||||||
|
func (api *API) GetStarTransactions(params GetStarTransactionsP) (StarTransactions, error) {
|
||||||
|
req := NewRequest[StarTransactions]("getStarTransactions", params)
|
||||||
|
return req.Do(api)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetStarTransactionsWithContext is the context-aware variant of GetStarTransactions.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#getstartransactions
|
||||||
|
func (api *API) GetStarTransactionsWithContext(ctx context.Context, params GetStarTransactionsP) (StarTransactions, error) {
|
||||||
|
req := NewRequest[StarTransactions]("getStarTransactions", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RefundStarPaymentP holds parameters for the refundStarPayment method.
|
||||||
|
// See https://core.telegram.org/bots/api#refundstarpayment
|
||||||
|
type RefundStarPaymentP struct {
|
||||||
|
UserID int64 `json:"user_id"`
|
||||||
|
TelegramPaymentChargeID string `json:"telegram_payment_charge_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// RefundStarPayment refunds a successful Telegram Stars payment.
|
||||||
|
// Returns true on success.
|
||||||
|
// See https://core.telegram.org/bots/api#refundstarpayment
|
||||||
|
func (api *API) RefundStarPayment(params RefundStarPaymentP) (bool, error) {
|
||||||
|
req := NewRequest[bool]("refundStarPayment", params)
|
||||||
|
return req.Do(api)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RefundStarPaymentWithContext is the context-aware variant of RefundStarPayment.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#refundstarpayment
|
||||||
|
func (api *API) RefundStarPaymentWithContext(ctx context.Context, params RefundStarPaymentP) (bool, error) {
|
||||||
|
req := NewRequest[bool]("refundStarPayment", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EditUserStarSubscriptionP holds parameters for the editUserStarSubscription method.
|
||||||
|
// See https://core.telegram.org/bots/api#edituserstarsubscription
|
||||||
|
type EditUserStarSubscriptionP struct {
|
||||||
|
UserID int64 `json:"user_id"`
|
||||||
|
TelegramPaymentChargeID string `json:"telegram_payment_charge_id"`
|
||||||
|
IsCanceled bool `json:"is_canceled"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// EditUserStarSubscription cancels or re-enables a user star subscription extension.
|
||||||
|
// Returns true on success.
|
||||||
|
// See https://core.telegram.org/bots/api#edituserstarsubscription
|
||||||
|
func (api *API) EditUserStarSubscription(params EditUserStarSubscriptionP) (bool, error) {
|
||||||
|
req := NewRequest[bool]("editUserStarSubscription", params)
|
||||||
|
return req.Do(api)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EditUserStarSubscriptionWithContext is the context-aware variant of EditUserStarSubscription.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#edituserstarsubscription
|
||||||
|
func (api *API) EditUserStarSubscriptionWithContext(ctx context.Context, params EditUserStarSubscriptionP) (bool, error) {
|
||||||
|
req := NewRequest[bool]("editUserStarSubscription", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package tgapi
|
||||||
|
|
||||||
|
// StarTransaction describes a Telegram Star transaction.
|
||||||
|
// See https://core.telegram.org/bots/api#startransaction
|
||||||
|
type StarTransaction struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Amount int `json:"amount"`
|
||||||
|
NanostarAmount int `json:"nanostar_amount,omitempty"`
|
||||||
|
Date int `json:"date"`
|
||||||
|
Source map[string]any `json:"source,omitempty"`
|
||||||
|
Receiver map[string]any `json:"receiver,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// StarTransactions contains a list of Telegram Star transactions.
|
||||||
|
// See https://core.telegram.org/bots/api#startransactions
|
||||||
|
type StarTransactions struct {
|
||||||
|
Transactions []StarTransaction `json:"transactions"`
|
||||||
|
}
|
||||||
+162
-7
@@ -1,5 +1,7 @@
|
|||||||
package tgapi
|
package tgapi
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
// SendStickerP holds parameters for the sendSticker method.
|
// SendStickerP holds parameters for the sendSticker method.
|
||||||
// See https://core.telegram.org/bots/api#sendsticker
|
// See https://core.telegram.org/bots/api#sendsticker
|
||||||
type SendStickerP struct {
|
type SendStickerP struct {
|
||||||
@@ -14,6 +16,10 @@ type SendStickerP struct {
|
|||||||
ProtectContent bool `json:"protect_content,omitempty"`
|
ProtectContent bool `json:"protect_content,omitempty"`
|
||||||
AllowPaidBroadcast bool `json:"allow_paid_broadcast,omitempty"`
|
AllowPaidBroadcast bool `json:"allow_paid_broadcast,omitempty"`
|
||||||
MessageEffectID string `json:"message_effect_id,omitempty"`
|
MessageEffectID string `json:"message_effect_id,omitempty"`
|
||||||
|
|
||||||
|
SuggestedPostParameters *SuggestedPostParameters `json:"suggested_post_parameters,omitempty"`
|
||||||
|
ReplyParameters *ReplyParameters `json:"reply_parameters,omitempty"`
|
||||||
|
ReplyMarkup *ReplyMarkup `json:"reply_markup,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendSticker sends a static .WEBP, animated .TGS, or video .WEBM sticker.
|
// SendSticker sends a static .WEBP, animated .TGS, or video .WEBM sticker.
|
||||||
@@ -23,6 +29,14 @@ func (api *API) SendSticker(params SendStickerP) (Message, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SendStickerWithContext is the context-aware variant of SendSticker.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#sendsticker
|
||||||
|
func (api *API) SendStickerWithContext(ctx context.Context, params SendStickerP) (Message, error) {
|
||||||
|
req := NewRequestWithChatID[Message]("sendSticker", params, params.ChatID)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// GetStickerSetP holds parameters for the getStickerSet method.
|
// GetStickerSetP holds parameters for the getStickerSet method.
|
||||||
// See https://core.telegram.org/bots/api#getstickerset
|
// See https://core.telegram.org/bots/api#getstickerset
|
||||||
type GetStickerSetP struct {
|
type GetStickerSetP struct {
|
||||||
@@ -36,6 +50,14 @@ func (api *API) GetStickerSet(params GetStickerSetP) (StickerSet, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetStickerSetWithContext is the context-aware variant of GetStickerSet.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#getstickerset
|
||||||
|
func (api *API) GetStickerSetWithContext(ctx context.Context, params GetStickerSetP) (StickerSet, error) {
|
||||||
|
req := NewRequest[StickerSet]("getStickerSet", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// GetCustomEmojiStickersP holds parameters for the getCustomEmojiStickers method.
|
// GetCustomEmojiStickersP holds parameters for the getCustomEmojiStickers method.
|
||||||
// See https://core.telegram.org/bots/api#getcustomemojistickers
|
// See https://core.telegram.org/bots/api#getcustomemojistickers
|
||||||
type GetCustomEmojiStickersP struct {
|
type GetCustomEmojiStickersP struct {
|
||||||
@@ -49,10 +71,49 @@ func (api *API) GetCustomEmojiStickers(params GetCustomEmojiStickersP) ([]Sticke
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetCustomEmojiStickersWithContext is the context-aware variant of GetCustomEmojiStickers.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#getcustomemojistickers
|
||||||
|
func (api *API) GetCustomEmojiStickersWithContext(ctx context.Context, params GetCustomEmojiStickersP) ([]Sticker, error) {
|
||||||
|
req := NewRequest[[]Sticker]("getCustomEmojiStickers", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UploadStickerFileP holds parameters for the uploadStickerFile method.
|
||||||
|
// See https://core.telegram.org/bots/api#uploadstickerfile
|
||||||
|
type UploadStickerFileP struct {
|
||||||
|
UserID int64 `json:"user_id"`
|
||||||
|
StickerFormat InputStickerFormat `json:"sticker_format"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UploadStickerFile uploads a sticker file for later use in sticker set methods.
|
||||||
|
// sticker is the file to upload.
|
||||||
|
// See https://core.telegram.org/bots/api#uploadstickerfile
|
||||||
|
func (api *API) UploadStickerFile(params UploadStickerFileP, sticker UploaderFile) (File, error) {
|
||||||
|
uploader := NewUploader(api)
|
||||||
|
defer func() {
|
||||||
|
_ = uploader.Close()
|
||||||
|
}()
|
||||||
|
req := NewUploaderRequest[File]("uploadStickerFile", params, sticker.SetType(UploaderStickerType))
|
||||||
|
return req.Do(uploader)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UploadStickerFileWithContext is the context-aware variant of UploadStickerFile.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#uploadstickerfile
|
||||||
|
func (api *API) UploadStickerFileWithContext(ctx context.Context, params UploadStickerFileP, sticker UploaderFile) (File, error) {
|
||||||
|
uploader := NewUploader(api)
|
||||||
|
defer func() {
|
||||||
|
_ = uploader.Close()
|
||||||
|
}()
|
||||||
|
req := NewUploaderRequest[File]("uploadStickerFile", params, sticker.SetType(UploaderStickerType))
|
||||||
|
return req.DoWithContext(ctx, uploader)
|
||||||
|
}
|
||||||
|
|
||||||
// CreateNewStickerSetP holds parameters for the createNewStickerSet method.
|
// CreateNewStickerSetP holds parameters for the createNewStickerSet method.
|
||||||
// See https://core.telegram.org/bots/api#createnewstickerset
|
// See https://core.telegram.org/bots/api#createnewstickerset
|
||||||
type CreateNewStickerSetP struct {
|
type CreateNewStickerSetP struct {
|
||||||
UserID int `json:"user_id"`
|
UserID int64 `json:"user_id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
|
|
||||||
@@ -69,10 +130,18 @@ func (api *API) CreateNewStickerSet(params CreateNewStickerSetP) (bool, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateNewStickerSetWithContext is the context-aware variant of CreateNewStickerSet.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#createnewstickerset
|
||||||
|
func (api *API) CreateNewStickerSetWithContext(ctx context.Context, params CreateNewStickerSetP) (bool, error) {
|
||||||
|
req := NewRequest[bool]("createNewStickerSet", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// AddStickerToSetP holds parameters for the addStickerToSet method.
|
// AddStickerToSetP holds parameters for the addStickerToSet method.
|
||||||
// See https://core.telegram.org/bots/api#addstickertoset
|
// See https://core.telegram.org/bots/api#addstickertoset
|
||||||
type AddStickerToSetP struct {
|
type AddStickerToSetP struct {
|
||||||
UserID int `json:"user_id"`
|
UserID int64 `json:"user_id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Sticker InputSticker `json:"sticker"`
|
Sticker InputSticker `json:"sticker"`
|
||||||
}
|
}
|
||||||
@@ -85,6 +154,14 @@ func (api *API) AddStickerToSet(params AddStickerToSetP) (bool, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddStickerToSetWithContext is the context-aware variant of AddStickerToSet.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#addstickertoset
|
||||||
|
func (api *API) AddStickerToSetWithContext(ctx context.Context, params AddStickerToSetP) (bool, error) {
|
||||||
|
req := NewRequest[bool]("addStickerToSet", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// SetStickerPositionInSetP holds parameters for the setStickerPositionInSet method.
|
// SetStickerPositionInSetP holds parameters for the setStickerPositionInSet method.
|
||||||
// See https://core.telegram.org/bots/api#setstickerpositioninset
|
// See https://core.telegram.org/bots/api#setstickerpositioninset
|
||||||
type SetStickerPositionInSetP struct {
|
type SetStickerPositionInSetP struct {
|
||||||
@@ -100,6 +177,14 @@ func (api *API) SetStickerPositionInSet(params SetStickerPositionInSetP) (bool,
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetStickerPositionInSetWithContext is the context-aware variant of SetStickerPositionInSet.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#setstickerpositioninset
|
||||||
|
func (api *API) SetStickerPositionInSetWithContext(ctx context.Context, params SetStickerPositionInSetP) (bool, error) {
|
||||||
|
req := NewRequest[bool]("setStickerPositionInSet", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// DeleteStickerFromSetP holds parameters for the deleteStickerFromSet method.
|
// DeleteStickerFromSetP holds parameters for the deleteStickerFromSet method.
|
||||||
// See https://core.telegram.org/bots/api#deletestickerfromset
|
// See https://core.telegram.org/bots/api#deletestickerfromset
|
||||||
type DeleteStickerFromSetP struct {
|
type DeleteStickerFromSetP struct {
|
||||||
@@ -114,10 +199,18 @@ func (api *API) DeleteStickerFromSet(params DeleteStickerFromSetP) (bool, error)
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteStickerFromSetWithContext is the context-aware variant of DeleteStickerFromSet.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#deletestickerfromset
|
||||||
|
func (api *API) DeleteStickerFromSetWithContext(ctx context.Context, params DeleteStickerFromSetP) (bool, error) {
|
||||||
|
req := NewRequest[bool]("deleteStickerFromSet", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// ReplaceStickerInSetP holds parameters for the replaceStickerInSet method.
|
// ReplaceStickerInSetP holds parameters for the replaceStickerInSet method.
|
||||||
// See https://core.telegram.org/bots/api#replacestickerinset
|
// See https://core.telegram.org/bots/api#replacestickerinset
|
||||||
type ReplaceStickerInSetP struct {
|
type ReplaceStickerInSetP struct {
|
||||||
UserID int `json:"user_id"`
|
UserID int64 `json:"user_id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
OldSticker string `json:"old_sticker"`
|
OldSticker string `json:"old_sticker"`
|
||||||
Sticker InputSticker `json:"sticker"`
|
Sticker InputSticker `json:"sticker"`
|
||||||
@@ -131,6 +224,14 @@ func (api *API) ReplaceStickerInSet(params ReplaceStickerInSetP) (bool, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReplaceStickerInSetWithContext is the context-aware variant of ReplaceStickerInSet.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#replacestickerinset
|
||||||
|
func (api *API) ReplaceStickerInSetWithContext(ctx context.Context, params ReplaceStickerInSetP) (bool, error) {
|
||||||
|
req := NewRequest[bool]("replaceStickerInSet", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// SetStickerEmojiListP holds parameters for the setStickerEmojiList method.
|
// SetStickerEmojiListP holds parameters for the setStickerEmojiList method.
|
||||||
// See https://core.telegram.org/bots/api#setstickeremojilist
|
// See https://core.telegram.org/bots/api#setstickeremojilist
|
||||||
type SetStickerEmojiListP struct {
|
type SetStickerEmojiListP struct {
|
||||||
@@ -146,6 +247,14 @@ func (api *API) SetStickerEmojiList(params SetStickerEmojiListP) (bool, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetStickerEmojiListWithContext is the context-aware variant of SetStickerEmojiList.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#setstickeremojilist
|
||||||
|
func (api *API) SetStickerEmojiListWithContext(ctx context.Context, params SetStickerEmojiListP) (bool, error) {
|
||||||
|
req := NewRequest[bool]("setStickerEmojiList", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// SetStickerKeywordsP holds parameters for the setStickerKeywords method.
|
// SetStickerKeywordsP holds parameters for the setStickerKeywords method.
|
||||||
// See https://core.telegram.org/bots/api#setstickerkeywords
|
// See https://core.telegram.org/bots/api#setstickerkeywords
|
||||||
type SetStickerKeywordsP struct {
|
type SetStickerKeywordsP struct {
|
||||||
@@ -161,6 +270,14 @@ func (api *API) SetStickerKeywords(params SetStickerKeywordsP) (bool, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetStickerKeywordsWithContext is the context-aware variant of SetStickerKeywords.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#setstickerkeywords
|
||||||
|
func (api *API) SetStickerKeywordsWithContext(ctx context.Context, params SetStickerKeywordsP) (bool, error) {
|
||||||
|
req := NewRequest[bool]("setStickerKeywords", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// SetStickerMaskPositionP holds parameters for the setStickerMaskPosition method.
|
// SetStickerMaskPositionP holds parameters for the setStickerMaskPosition method.
|
||||||
// See https://core.telegram.org/bots/api#setstickermaskposition
|
// See https://core.telegram.org/bots/api#setstickermaskposition
|
||||||
type SetStickerMaskPositionP struct {
|
type SetStickerMaskPositionP struct {
|
||||||
@@ -176,6 +293,14 @@ func (api *API) SetStickerMaskPosition(params SetStickerMaskPositionP) (bool, er
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetStickerMaskPositionWithContext is the context-aware variant of SetStickerMaskPosition.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#setstickermaskposition
|
||||||
|
func (api *API) SetStickerMaskPositionWithContext(ctx context.Context, params SetStickerMaskPositionP) (bool, error) {
|
||||||
|
req := NewRequest[bool]("setStickerMaskPosition", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// SetStickerSetTitleP holds parameters for the setStickerSetTitle method.
|
// SetStickerSetTitleP holds parameters for the setStickerSetTitle method.
|
||||||
// See https://core.telegram.org/bots/api#setstickersettitle
|
// See https://core.telegram.org/bots/api#setstickersettitle
|
||||||
type SetStickerSetTitleP struct {
|
type SetStickerSetTitleP struct {
|
||||||
@@ -191,11 +316,19 @@ func (api *API) SetStickerSetTitle(params SetStickerSetTitleP) (bool, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetStickerSetTitleWithContext is the context-aware variant of SetStickerSetTitle.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#setstickersettitle
|
||||||
|
func (api *API) SetStickerSetTitleWithContext(ctx context.Context, params SetStickerSetTitleP) (bool, error) {
|
||||||
|
req := NewRequest[bool]("setStickerSetTitle", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// SetStickerSetThumbnailP holds parameters for the setStickerSetThumbnail method.
|
// SetStickerSetThumbnailP holds parameters for the setStickerSetThumbnail method.
|
||||||
// See https://core.telegram.org/bots/api#setstickersetthumbnail
|
// See https://core.telegram.org/bots/api#setstickersetthumbnail
|
||||||
type SetStickerSetThumbnailP struct {
|
type SetStickerSetThumbnailP struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
UserID int `json:"user_id"`
|
UserID int64 `json:"user_id"`
|
||||||
Thumbnail string `json:"thumbnail"`
|
Thumbnail string `json:"thumbnail"`
|
||||||
Format InputStickerFormat `json:"format"`
|
Format InputStickerFormat `json:"format"`
|
||||||
}
|
}
|
||||||
@@ -208,6 +341,14 @@ func (api *API) SetStickerSetThumbnail(params SetStickerSetThumbnailP) (bool, er
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetStickerSetThumbnailWithContext is the context-aware variant of SetStickerSetThumbnail.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#setstickersetthumbnail
|
||||||
|
func (api *API) SetStickerSetThumbnailWithContext(ctx context.Context, params SetStickerSetThumbnailP) (bool, error) {
|
||||||
|
req := NewRequest[bool]("setStickerSetThumbnail", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// SetCustomEmojiStickerSetThumbnailP holds parameters for the setCustomEmojiStickerSetThumbnail method.
|
// SetCustomEmojiStickerSetThumbnailP holds parameters for the setCustomEmojiStickerSetThumbnail method.
|
||||||
// See https://core.telegram.org/bots/api#setcustomemojistickersetthumbnail
|
// See https://core.telegram.org/bots/api#setcustomemojistickersetthumbnail
|
||||||
type SetCustomEmojiStickerSetThumbnailP struct {
|
type SetCustomEmojiStickerSetThumbnailP struct {
|
||||||
@@ -218,13 +359,19 @@ type SetCustomEmojiStickerSetThumbnailP struct {
|
|||||||
// SetCustomEmojiStickerSetThumbnail sets the thumbnail of a custom emoji sticker set.
|
// SetCustomEmojiStickerSetThumbnail sets the thumbnail of a custom emoji sticker set.
|
||||||
// Returns True on success.
|
// Returns True on success.
|
||||||
// See https://core.telegram.org/bots/api#setcustomemojistickersetthumbnail
|
// See https://core.telegram.org/bots/api#setcustomemojistickersetthumbnail
|
||||||
//
|
func (api *API) SetCustomEmojiStickerSetThumbnail(params SetCustomEmojiStickerSetThumbnailP) (bool, error) {
|
||||||
// Note: This method uses SetStickerSetThumbnailP as its parameter type, which might be inconsistent.
|
|
||||||
func (api *API) SetCustomEmojiStickerSetThumbnail(params SetStickerSetThumbnailP) (bool, error) {
|
|
||||||
req := NewRequest[bool]("setCustomEmojiStickerSetThumbnail", params)
|
req := NewRequest[bool]("setCustomEmojiStickerSetThumbnail", params)
|
||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetCustomEmojiStickerSetThumbnailWithContext is the context-aware variant of SetCustomEmojiStickerSetThumbnail.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#setcustomemojistickersetthumbnail
|
||||||
|
func (api *API) SetCustomEmojiStickerSetThumbnailWithContext(ctx context.Context, params SetCustomEmojiStickerSetThumbnailP) (bool, error) {
|
||||||
|
req := NewRequest[bool]("setCustomEmojiStickerSetThumbnail", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// DeleteStickerSetP holds parameters for the deleteStickerSet method.
|
// DeleteStickerSetP holds parameters for the deleteStickerSet method.
|
||||||
// See https://core.telegram.org/bots/api#deletestickerset
|
// See https://core.telegram.org/bots/api#deletestickerset
|
||||||
type DeleteStickerSetP struct {
|
type DeleteStickerSetP struct {
|
||||||
@@ -238,3 +385,11 @@ func (api *API) DeleteStickerSet(params DeleteStickerSetP) (bool, error) {
|
|||||||
req := NewRequest[bool]("deleteStickerSet", params)
|
req := NewRequest[bool]("deleteStickerSet", params)
|
||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteStickerSetWithContext is the context-aware variant of DeleteStickerSet.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#deletestickerset
|
||||||
|
func (api *API) DeleteStickerSetWithContext(ctx context.Context, params DeleteStickerSetP) (bool, error) {
|
||||||
|
req := NewRequest[bool]("deleteStickerSet", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ type Sticker struct {
|
|||||||
MaskPosition *MaskPosition `json:"mask_position,omitempty"`
|
MaskPosition *MaskPosition `json:"mask_position,omitempty"`
|
||||||
CustomEmojiID *string `json:"custom_emoji_id,omitempty"`
|
CustomEmojiID *string `json:"custom_emoji_id,omitempty"`
|
||||||
NeedRepainting *bool `json:"need_repainting,omitempty"`
|
NeedRepainting *bool `json:"need_repainting,omitempty"`
|
||||||
FileSize *int `json:"file_size,omitempty"`
|
FileSize *int64 `json:"file_size,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// StickerSet represents a sticker set.
|
// StickerSet represents a sticker set.
|
||||||
|
|||||||
+46
-12
@@ -1,5 +1,7 @@
|
|||||||
package tgapi
|
package tgapi
|
||||||
|
|
||||||
|
import "encoding/json"
|
||||||
|
|
||||||
// UpdateType represents the type of incoming update.
|
// UpdateType represents the type of incoming update.
|
||||||
type UpdateType string
|
type UpdateType string
|
||||||
|
|
||||||
@@ -23,8 +25,10 @@ const (
|
|||||||
UpdateTypeBusinessMessage UpdateType = "business_message"
|
UpdateTypeBusinessMessage UpdateType = "business_message"
|
||||||
// UpdateTypeEditedBusinessMessage is an edited business message update.
|
// UpdateTypeEditedBusinessMessage is an edited business message update.
|
||||||
UpdateTypeEditedBusinessMessage UpdateType = "edited_business_message"
|
UpdateTypeEditedBusinessMessage UpdateType = "edited_business_message"
|
||||||
// UpdateTypeDeletedBusinessMessage is a deleted business message update.
|
// UpdateTypeDeletedBusinessMessages is a deleted business messages update.
|
||||||
UpdateTypeDeletedBusinessMessage UpdateType = "deleted_business_message"
|
UpdateTypeDeletedBusinessMessages UpdateType = "deleted_business_messages"
|
||||||
|
// UpdateTypeDeletedBusinessMessage is kept as a backward-compatible alias.
|
||||||
|
UpdateTypeDeletedBusinessMessage UpdateType = UpdateTypeDeletedBusinessMessages
|
||||||
|
|
||||||
// UpdateTypeInlineQuery is an inline query update.
|
// UpdateTypeInlineQuery is an inline query update.
|
||||||
UpdateTypeInlineQuery UpdateType = "inline_query"
|
UpdateTypeInlineQuery UpdateType = "inline_query"
|
||||||
@@ -63,17 +67,18 @@ type Update struct {
|
|||||||
ChannelPost *Message `json:"channel_post,omitempty"`
|
ChannelPost *Message `json:"channel_post,omitempty"`
|
||||||
EditedChannelPost *Message `json:"edited_channel_post,omitempty"`
|
EditedChannelPost *Message `json:"edited_channel_post,omitempty"`
|
||||||
|
|
||||||
BusinessConnection *BusinessConnection `json:"business_connection,omitempty"`
|
BusinessConnection *BusinessConnection `json:"business_connection,omitempty"`
|
||||||
BusinessMessage *Message `json:"business_message,omitempty"`
|
BusinessMessage *Message `json:"business_message,omitempty"`
|
||||||
EditedBusinessMessage *Message `json:"edited_business_message,omitempty"`
|
EditedBusinessMessage *Message `json:"edited_business_message,omitempty"`
|
||||||
DeletedBusinessMessage *Message `json:"deleted_business_messages,omitempty"`
|
DeletedBusinessMessages *BusinessMessagesDeleted `json:"deleted_business_messages,omitempty"`
|
||||||
MessageReaction *MessageReactionUpdated `json:"message_reaction,omitempty"`
|
DeletedBusinessMessage *BusinessMessagesDeleted `json:"-"`
|
||||||
MessageReactionCount *MessageReactionCountUpdated `json:"message_reaction_count,omitempty"`
|
MessageReaction *MessageReactionUpdated `json:"message_reaction,omitempty"`
|
||||||
|
MessageReactionCount *MessageReactionCountUpdated `json:"message_reaction_count,omitempty"`
|
||||||
|
|
||||||
InlineQuery *InlineQuery `json:"inline_query,omitempty"`
|
InlineQuery *InlineQuery `json:"inline_query,omitempty"`
|
||||||
ChosenInlineResult *ChosenInlineResult `json:"chosen_inline_result,omitempty"`
|
ChosenInlineResult *ChosenInlineResult `json:"chosen_inline_result,omitempty"`
|
||||||
CallbackQuery *CallbackQuery `json:"callback_query,omitempty"`
|
CallbackQuery *CallbackQuery `json:"callback_query,omitempty"`
|
||||||
ShippingQuery ShippingQuery `json:"shipping_query,omitempty"`
|
ShippingQuery *ShippingQuery `json:"shipping_query,omitempty"`
|
||||||
PreCheckoutQuery *PreCheckoutQuery `json:"pre_checkout_query,omitempty"`
|
PreCheckoutQuery *PreCheckoutQuery `json:"pre_checkout_query,omitempty"`
|
||||||
PurchasedPaidMedia *PaidMediaPurchased `json:"purchased_paid_media,omitempty"`
|
PurchasedPaidMedia *PaidMediaPurchased `json:"purchased_paid_media,omitempty"`
|
||||||
|
|
||||||
@@ -86,6 +91,35 @@ type Update struct {
|
|||||||
RemovedChatBoost *ChatBoostRemoved `json:"removed_chat_boost,omitempty"`
|
RemovedChatBoost *ChatBoostRemoved `json:"removed_chat_boost,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *Update) syncDeletedBusinessMessages() {
|
||||||
|
if u.DeletedBusinessMessages != nil {
|
||||||
|
u.DeletedBusinessMessage = u.DeletedBusinessMessages
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if u.DeletedBusinessMessage != nil {
|
||||||
|
u.DeletedBusinessMessages = u.DeletedBusinessMessage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON keeps the deprecated DeletedBusinessMessage alias in sync.
|
||||||
|
func (u *Update) UnmarshalJSON(data []byte) error {
|
||||||
|
type alias Update
|
||||||
|
var aux alias
|
||||||
|
if err := json.Unmarshal(data, &aux); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*u = Update(aux)
|
||||||
|
u.syncDeletedBusinessMessages()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON emits the canonical deleted_business_messages field.
|
||||||
|
func (u Update) MarshalJSON() ([]byte, error) {
|
||||||
|
u.syncDeletedBusinessMessages()
|
||||||
|
type alias Update
|
||||||
|
return json.Marshal(alias(u))
|
||||||
|
}
|
||||||
|
|
||||||
// InlineQuery represents an incoming inline query.
|
// InlineQuery represents an incoming inline query.
|
||||||
// See https://core.telegram.org/bots/api#inlinequery
|
// See https://core.telegram.org/bots/api#inlinequery
|
||||||
type InlineQuery struct {
|
type InlineQuery struct {
|
||||||
@@ -160,7 +194,7 @@ type PaidMediaPurchased struct {
|
|||||||
type File struct {
|
type File struct {
|
||||||
FileId string `json:"file_id"`
|
FileId string `json:"file_id"`
|
||||||
FileUniqueID string `json:"file_unique_id"`
|
FileUniqueID string `json:"file_unique_id"`
|
||||||
FileSize int `json:"file_size,omitempty"`
|
FileSize int64 `json:"file_size,omitempty"`
|
||||||
FilePath string `json:"file_path,omitempty"`
|
FilePath string `json:"file_path,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,7 +209,7 @@ type Audio struct {
|
|||||||
Title string `json:"title,omitempty"`
|
Title string `json:"title,omitempty"`
|
||||||
FileName string `json:"file_name,omitempty"`
|
FileName string `json:"file_name,omitempty"`
|
||||||
MimeType string `json:"mime_type,omitempty"`
|
MimeType string `json:"mime_type,omitempty"`
|
||||||
FileSize int `json:"file_size,omitempty"`
|
FileSize int64 `json:"file_size,omitempty"`
|
||||||
Thumbnail *PhotoSize `json:"thumbnail,omitempty"`
|
Thumbnail *PhotoSize `json:"thumbnail,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -234,7 +268,7 @@ type ChatMemberUpdated struct {
|
|||||||
type ChatJoinRequest struct {
|
type ChatJoinRequest struct {
|
||||||
Chat Chat `json:"chat"`
|
Chat Chat `json:"chat"`
|
||||||
From User `json:"from"`
|
From User `json:"from"`
|
||||||
UserChatID int `json:"user_chat_id"`
|
UserChatID int64 `json:"user_chat_id"`
|
||||||
Date int64 `json:"date"`
|
Date int64 `json:"date"`
|
||||||
Bio *string `json:"bio,omitempty"`
|
Bio *string `json:"bio,omitempty"`
|
||||||
InviteLink *ChatInviteLink `json:"invite_link,omitempty"`
|
InviteLink *ChatInviteLink `json:"invite_link,omitempty"`
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
package tgapi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestUpdateDeletedBusinessMessagesUnmarshalSetsAlias(t *testing.T) {
|
||||||
|
var update Update
|
||||||
|
err := json.Unmarshal([]byte(`{
|
||||||
|
"update_id": 1,
|
||||||
|
"deleted_business_messages": {
|
||||||
|
"business_connection_id": "conn",
|
||||||
|
"chat": {"id": 42, "type": "private"},
|
||||||
|
"message_ids": [3, 5]
|
||||||
|
}
|
||||||
|
}`), &update)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unmarshal returned error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if update.DeletedBusinessMessages == nil {
|
||||||
|
t.Fatal("expected DeletedBusinessMessages to be populated")
|
||||||
|
}
|
||||||
|
if update.DeletedBusinessMessage == nil {
|
||||||
|
t.Fatal("expected deprecated DeletedBusinessMessage alias to be populated")
|
||||||
|
}
|
||||||
|
if update.DeletedBusinessMessages != update.DeletedBusinessMessage {
|
||||||
|
t.Fatal("expected deleted business message fields to share the same payload")
|
||||||
|
}
|
||||||
|
if got := update.DeletedBusinessMessages.MessageIDs; len(got) != 2 || got[0] != 3 || got[1] != 5 {
|
||||||
|
t.Fatalf("unexpected message ids: %v", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdateMarshalUsesCanonicalDeletedBusinessMessagesField(t *testing.T) {
|
||||||
|
update := Update{
|
||||||
|
UpdateID: 1,
|
||||||
|
DeletedBusinessMessage: &BusinessMessagesDeleted{
|
||||||
|
BusinessConnectionID: "conn",
|
||||||
|
Chat: Chat{ID: 42, Type: string(ChatTypePrivate)},
|
||||||
|
MessageIDs: []int{7},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := json.Marshal(update)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Marshal returned error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
got := string(data)
|
||||||
|
if !strings.Contains(got, `"deleted_business_messages"`) {
|
||||||
|
t.Fatalf("expected canonical deleted_business_messages field, got %s", got)
|
||||||
|
}
|
||||||
|
if strings.Contains(got, `"deleted_business_message"`) {
|
||||||
|
t.Fatalf("unexpected singular deleted_business_message field, got %s", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdateShippingQueryIsNilWhenAbsent(t *testing.T) {
|
||||||
|
var update Update
|
||||||
|
if err := json.Unmarshal([]byte(`{"update_id":1}`), &update); err != nil {
|
||||||
|
t.Fatalf("Unmarshal returned error: %v", err)
|
||||||
|
}
|
||||||
|
if update.ShippingQuery != nil {
|
||||||
|
t.Fatalf("expected ShippingQuery to be nil, got %+v", update.ShippingQuery)
|
||||||
|
}
|
||||||
|
}
|
||||||
+72
-35
@@ -3,7 +3,6 @@ package tgapi
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"mime/multipart"
|
"mime/multipart"
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -15,47 +14,76 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
UploaderPhotoType UploaderFileType = "photo"
|
// UploaderPhotoType is the multipart field name for photo uploads.
|
||||||
UploaderVideoType UploaderFileType = "video"
|
UploaderPhotoType UploaderFileType = "photo"
|
||||||
UploaderAudioType UploaderFileType = "audio"
|
// UploaderVideoType is the multipart field name for video uploads.
|
||||||
UploaderDocumentType UploaderFileType = "document"
|
UploaderVideoType UploaderFileType = "video"
|
||||||
UploaderVoiceType UploaderFileType = "voice"
|
// UploaderAudioType is the multipart field name for audio uploads.
|
||||||
|
UploaderAudioType UploaderFileType = "audio"
|
||||||
|
// UploaderDocumentType is the multipart field name for document uploads.
|
||||||
|
UploaderDocumentType UploaderFileType = "document"
|
||||||
|
// UploaderVoiceType is the multipart field name for voice uploads.
|
||||||
|
UploaderVoiceType UploaderFileType = "voice"
|
||||||
|
// UploaderVideoNoteType is the multipart field name for video-note uploads.
|
||||||
UploaderVideoNoteType UploaderFileType = "video_note"
|
UploaderVideoNoteType UploaderFileType = "video_note"
|
||||||
|
// UploaderThumbnailType is the multipart field name for thumbnail uploads.
|
||||||
UploaderThumbnailType UploaderFileType = "thumbnail"
|
UploaderThumbnailType UploaderFileType = "thumbnail"
|
||||||
|
// UploaderStickerType is the multipart field name for sticker uploads.
|
||||||
|
UploaderStickerType UploaderFileType = "sticker"
|
||||||
|
// UploaderCertificateType is the multipart field name for webhook certificate uploads.
|
||||||
|
UploaderCertificateType UploaderFileType = "certificate"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// UploaderFileType represents the Telegram form field name for a file upload.
|
||||||
type UploaderFileType string
|
type UploaderFileType string
|
||||||
|
|
||||||
|
// UploaderFile holds the data and metadata for a single file to be uploaded.
|
||||||
type UploaderFile struct {
|
type UploaderFile struct {
|
||||||
filename string
|
filename string
|
||||||
data []byte
|
data []byte
|
||||||
field UploaderFileType
|
field UploaderFileType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewUploaderFile creates a new UploaderFile, auto-detecting the field type from the file extension.
|
||||||
|
// If detection is incorrect, use SetType to override.
|
||||||
func NewUploaderFile(name string, data []byte) UploaderFile {
|
func NewUploaderFile(name string, data []byte) UploaderFile {
|
||||||
t := uploaderTypeByExt(name)
|
t := uploaderTypeByExt(name)
|
||||||
return UploaderFile{filename: name, data: data, field: t}
|
return UploaderFile{filename: name, data: data, field: t}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetType used when auto-detect failed.
|
// SetType overrides the auto-detected upload field type.
|
||||||
// i.e. you sending a voice message, but it detects as audio, or if you send audio with thumbnail
|
// For example, use it when a voice file is detected as audio.
|
||||||
func (f UploaderFile) SetType(t UploaderFileType) UploaderFile {
|
func (f UploaderFile) SetType(t UploaderFileType) UploaderFile {
|
||||||
f.field = t
|
f.field = t
|
||||||
return f
|
return f
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Uploader is a Telegram Bot API client specialized for multipart file uploads.
|
||||||
|
//
|
||||||
|
// Use Uploader methods when you need to upload binary files directly
|
||||||
|
// (InputFile/multipart). For JSON-only calls (file_id, URL, plain params), use API.
|
||||||
type Uploader struct {
|
type Uploader struct {
|
||||||
api *API
|
api *API
|
||||||
logger *slog.Logger
|
logger *slog.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewUploader creates a multipart uploader bound to an API client.
|
||||||
func NewUploader(api *API) *Uploader {
|
func NewUploader(api *API) *Uploader {
|
||||||
logger := slog.CreateLogger().Level(utils.GetLoggerLevel()).Prefix("UPLOADER")
|
logger := slog.CreateLogger().Level(utils.GetLoggerLevel()).Prefix("UPLOADER")
|
||||||
logger.AddWriter(logger.CreateJsonStdoutWriter())
|
logger.AddWriter(logger.CreateJsonStdoutWriter())
|
||||||
return &Uploader{api, logger}
|
return &Uploader{api, logger}
|
||||||
}
|
}
|
||||||
func (u *Uploader) Close() error { return u.logger.Close() }
|
|
||||||
|
// Close flushes and closes uploader logger resources.
|
||||||
|
// See https://core.telegram.org/bots/api
|
||||||
|
func (u *Uploader) Close() error { return u.logger.Close() }
|
||||||
|
|
||||||
|
// GetLogger returns uploader logger instance.
|
||||||
|
// See https://core.telegram.org/bots/api
|
||||||
func (u *Uploader) GetLogger() *slog.Logger { return u.logger }
|
func (u *Uploader) GetLogger() *slog.Logger { return u.logger }
|
||||||
|
|
||||||
|
// UploaderRequest is a multipart file upload request to the Telegram API.
|
||||||
|
// Use NewUploaderRequest or NewUploaderRequestWithChatID to construct one.
|
||||||
type UploaderRequest[R, P any] struct {
|
type UploaderRequest[R, P any] struct {
|
||||||
method string
|
method string
|
||||||
files []UploaderFile
|
files []UploaderFile
|
||||||
@@ -63,48 +91,45 @@ type UploaderRequest[R, P any] struct {
|
|||||||
chatId int64
|
chatId int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewUploaderRequest creates a new multipart upload request with no associated chat ID.
|
||||||
func NewUploaderRequest[R, P any](method string, params P, files ...UploaderFile) UploaderRequest[R, P] {
|
func NewUploaderRequest[R, P any](method string, params P, files ...UploaderFile) UploaderRequest[R, P] {
|
||||||
return UploaderRequest[R, P]{method: method, files: files, params: params, chatId: 0}
|
return UploaderRequest[R, P]{method: method, files: files, params: params, chatId: 0}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewUploaderRequestWithChatID creates a new multipart upload request with an associated chat ID.
|
||||||
|
// The chat ID is used for per-chat rate limiting.
|
||||||
func NewUploaderRequestWithChatID[R, P any](method string, params P, chatId int64, files ...UploaderFile) UploaderRequest[R, P] {
|
func NewUploaderRequestWithChatID[R, P any](method string, params P, chatId int64, files ...UploaderFile) UploaderRequest[R, P] {
|
||||||
return UploaderRequest[R, P]{method: method, files: files, params: params, chatId: chatId}
|
return UploaderRequest[R, P]{method: method, files: files, params: params, chatId: chatId}
|
||||||
}
|
}
|
||||||
func (r UploaderRequest[R, P]) doRequest(ctx context.Context, up *Uploader) (R, error) {
|
func (r UploaderRequest[R, P]) doRequest(ctx context.Context, up *Uploader) (R, error) {
|
||||||
var zero R
|
var zero R
|
||||||
|
|
||||||
buf, contentType, err := prepareMultipart(r.files, r.params)
|
|
||||||
if err != nil {
|
|
||||||
return zero, err
|
|
||||||
}
|
|
||||||
|
|
||||||
methodPrefix := ""
|
methodPrefix := ""
|
||||||
if up.api.useTestServer {
|
if up.api.useTestServer {
|
||||||
methodPrefix = "/test"
|
methodPrefix = "/test"
|
||||||
}
|
}
|
||||||
url := fmt.Sprintf("%s/bot%s%s/%s", up.api.apiUrl, up.api.token, methodPrefix, r.method)
|
url := fmt.Sprintf("%s/bot%s%s/%s", up.api.apiUrl, up.api.token, methodPrefix, r.method)
|
||||||
req, err := http.NewRequestWithContext(ctx, "POST", url, buf)
|
|
||||||
if err != nil {
|
|
||||||
return zero, err
|
|
||||||
}
|
|
||||||
req.Header.Set("Content-Type", contentType)
|
|
||||||
req.Header.Set("Accept", "application/json")
|
|
||||||
req.Header.Set("User-Agent", fmt.Sprintf("Laniakea/%s", utils.VersionString))
|
|
||||||
req.Header.Set("Accept-Encoding", "gzip")
|
|
||||||
req.ContentLength = int64(buf.Len())
|
|
||||||
|
|
||||||
for {
|
for {
|
||||||
if up.api.Limiter != nil {
|
if up.api.Limiter != nil {
|
||||||
if up.api.dropOverflowLimit {
|
if err := up.api.Limiter.Check(ctx, up.api.dropOverflowLimit, r.chatId); err != nil {
|
||||||
if !up.api.Limiter.GlobalAllow() {
|
return zero, err
|
||||||
return zero, errors.New("rate limited")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if err := up.api.Limiter.GlobalWait(ctx); err != nil {
|
|
||||||
return zero, err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
buf, contentType, err := prepareMultipart(r.files, r.params)
|
||||||
|
if err != nil {
|
||||||
|
return zero, err
|
||||||
|
}
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "POST", url, buf)
|
||||||
|
if err != nil {
|
||||||
|
return zero, err
|
||||||
|
}
|
||||||
|
req.Header.Set("Content-Type", contentType)
|
||||||
|
req.Header.Set("Accept", "application/json")
|
||||||
|
req.Header.Set("User-Agent", fmt.Sprintf("Laniakea/%s", utils.VersionString))
|
||||||
|
req.ContentLength = int64(buf.Len())
|
||||||
|
|
||||||
up.logger.Debugln("UPLOADER REQ", r.method)
|
up.logger.Debugln("UPLOADER REQ", r.method)
|
||||||
resp, err := up.api.client.Do(req)
|
resp, err := up.api.client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -127,10 +152,12 @@ func (r UploaderRequest[R, P]) doRequest(ctx context.Context, up *Uploader) (R,
|
|||||||
if response.ErrorCode == 429 && response.Parameters != nil && response.Parameters.RetryAfter != nil {
|
if response.ErrorCode == 429 && response.Parameters != nil && response.Parameters.RetryAfter != nil {
|
||||||
after := *response.Parameters.RetryAfter
|
after := *response.Parameters.RetryAfter
|
||||||
up.logger.Warnf("Rate limited, retry after %d seconds (chat: %d)", after, r.chatId)
|
up.logger.Warnf("Rate limited, retry after %d seconds (chat: %d)", after, r.chatId)
|
||||||
if r.chatId > 0 {
|
if up.api.Limiter != nil {
|
||||||
up.api.Limiter.SetChatLock(r.chatId, after)
|
if r.chatId > 0 {
|
||||||
} else {
|
up.api.Limiter.SetChatLock(r.chatId, after)
|
||||||
up.api.Limiter.SetGlobalLock(after)
|
} else {
|
||||||
|
up.api.Limiter.SetGlobalLock(after)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
@@ -145,6 +172,9 @@ func (r UploaderRequest[R, P]) doRequest(ctx context.Context, up *Uploader) (R,
|
|||||||
return response.Result, nil
|
return response.Result, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DoWithContext executes the upload request asynchronously via the worker pool.
|
||||||
|
// Returns the result or error. Respects context cancellation.
|
||||||
func (r UploaderRequest[R, P]) DoWithContext(ctx context.Context, up *Uploader) (R, error) {
|
func (r UploaderRequest[R, P]) DoWithContext(ctx context.Context, up *Uploader) (R, error) {
|
||||||
var zero R
|
var zero R
|
||||||
|
|
||||||
@@ -168,10 +198,15 @@ func (r UploaderRequest[R, P]) DoWithContext(ctx context.Context, up *Uploader)
|
|||||||
return zero, ErrPoolUnexpected
|
return zero, ErrPoolUnexpected
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Do executes the upload request synchronously with a background context.
|
||||||
|
// Use only for simple, non-critical uploads.
|
||||||
func (r UploaderRequest[R, P]) Do(up *Uploader) (R, error) {
|
func (r UploaderRequest[R, P]) Do(up *Uploader) (R, error) {
|
||||||
return r.DoWithContext(context.Background(), up)
|
return r.DoWithContext(context.Background(), up)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// prepareMultipart builds a multipart form body from the given files and params.
|
||||||
|
// Params are encoded via utils.Encode. The writer boundary is finalized before returning.
|
||||||
func prepareMultipart[P any](files []UploaderFile, params P) (*bytes.Buffer, string, error) {
|
func prepareMultipart[P any](files []UploaderFile, params P) (*bytes.Buffer, string, error) {
|
||||||
buf := bytes.NewBuffer(nil)
|
buf := bytes.NewBuffer(nil)
|
||||||
w := multipart.NewWriter(buf)
|
w := multipart.NewWriter(buf)
|
||||||
@@ -204,6 +239,8 @@ func prepareMultipart[P any](files []UploaderFile, params P) (*bytes.Buffer, str
|
|||||||
return buf, w.FormDataContentType(), nil
|
return buf, w.FormDataContentType(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// uploaderTypeByExt infers the Telegram upload field name from a file extension.
|
||||||
|
// Falls back to UploaderDocumentType for unrecognized extensions.
|
||||||
func uploaderTypeByExt(filename string) UploaderFileType {
|
func uploaderTypeByExt(filename string) UploaderFileType {
|
||||||
ext := filepath.Ext(filename)
|
ext := filepath.Ext(filename)
|
||||||
switch ext {
|
switch ext {
|
||||||
|
|||||||
@@ -0,0 +1,138 @@
|
|||||||
|
package tgapi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"mime"
|
||||||
|
"mime/multipart"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestUploaderEncodesJSONFieldsAndLeavesAcceptEncodingToHTTPTransport(t *testing.T) {
|
||||||
|
var (
|
||||||
|
gotPath string
|
||||||
|
gotAcceptEncoding string
|
||||||
|
gotFields map[string]string
|
||||||
|
gotFileName string
|
||||||
|
gotFileData []byte
|
||||||
|
roundTripErr error
|
||||||
|
)
|
||||||
|
|
||||||
|
client := &http.Client{
|
||||||
|
Transport: roundTripFunc(func(req *http.Request) (*http.Response, error) {
|
||||||
|
gotPath = req.URL.Path
|
||||||
|
gotAcceptEncoding = req.Header.Get("Accept-Encoding")
|
||||||
|
|
||||||
|
gotFields, gotFileName, gotFileData, roundTripErr = readMultipartRequest(req)
|
||||||
|
if roundTripErr != nil {
|
||||||
|
roundTripErr = fmt.Errorf("readMultipartRequest: %w", roundTripErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &http.Response{
|
||||||
|
StatusCode: http.StatusOK,
|
||||||
|
Header: http.Header{"Content-Type": []string{"application/json"}},
|
||||||
|
Body: io.NopCloser(strings.NewReader(`{"ok":true,"result":{"message_id":5,"date":1}}`)),
|
||||||
|
}, nil
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
api := NewAPI(
|
||||||
|
NewAPIOpts("token").
|
||||||
|
SetAPIUrl("https://example.test").
|
||||||
|
SetHTTPClient(client),
|
||||||
|
)
|
||||||
|
defer func() {
|
||||||
|
if err := api.CloseApi(); err != nil {
|
||||||
|
t.Fatalf("CloseApi returned error: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
uploader := NewUploader(api)
|
||||||
|
defer func() {
|
||||||
|
if err := uploader.Close(); err != nil {
|
||||||
|
t.Fatalf("Close returned error: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
msg, err := uploader.SendPhoto(
|
||||||
|
UploadPhotoP{
|
||||||
|
ChatID: 42,
|
||||||
|
CaptionEntities: []MessageEntity{{
|
||||||
|
Type: MessageEntityBold,
|
||||||
|
Offset: 0,
|
||||||
|
Length: 4,
|
||||||
|
}},
|
||||||
|
ReplyMarkup: &ReplyMarkup{
|
||||||
|
InlineKeyboard: [][]InlineKeyboardButton{{
|
||||||
|
{Text: "A", CallbackData: "b"},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
NewUploaderFile("photo.jpg", []byte("img")),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("SendPhoto returned error: %v", err)
|
||||||
|
}
|
||||||
|
if msg.MessageID != 5 {
|
||||||
|
t.Fatalf("unexpected message id: %d", msg.MessageID)
|
||||||
|
}
|
||||||
|
if roundTripErr != nil {
|
||||||
|
t.Fatalf("multipart parse failed: %v", roundTripErr)
|
||||||
|
}
|
||||||
|
if gotPath != "/bottoken/sendPhoto" {
|
||||||
|
t.Fatalf("unexpected request path: %s", gotPath)
|
||||||
|
}
|
||||||
|
if gotAcceptEncoding != "" {
|
||||||
|
t.Fatalf("expected empty Accept-Encoding header, got %q", gotAcceptEncoding)
|
||||||
|
}
|
||||||
|
if got := gotFields["chat_id"]; got != "42" {
|
||||||
|
t.Fatalf("chat_id mismatch: %q", got)
|
||||||
|
}
|
||||||
|
if got := gotFields["caption_entities"]; got != `[{"type":"bold","offset":0,"length":4}]` {
|
||||||
|
t.Fatalf("caption_entities mismatch: %q", got)
|
||||||
|
}
|
||||||
|
if got := gotFields["reply_markup"]; got != `{"inline_keyboard":[[{"text":"A","callback_data":"b"}]]}` {
|
||||||
|
t.Fatalf("reply_markup mismatch: %q", got)
|
||||||
|
}
|
||||||
|
if gotFileName != "photo.jpg" {
|
||||||
|
t.Fatalf("unexpected file name: %q", gotFileName)
|
||||||
|
}
|
||||||
|
if string(gotFileData) != "img" {
|
||||||
|
t.Fatalf("unexpected file content: %q", string(gotFileData))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func readMultipartRequest(req *http.Request) (map[string]string, string, []byte, error) {
|
||||||
|
_, params, err := mime.ParseMediaType(req.Header.Get("Content-Type"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", nil, err
|
||||||
|
}
|
||||||
|
reader := multipart.NewReader(req.Body, params["boundary"])
|
||||||
|
|
||||||
|
fields := make(map[string]string)
|
||||||
|
var fileName string
|
||||||
|
var fileData []byte
|
||||||
|
for {
|
||||||
|
part, err := reader.NextPart()
|
||||||
|
if err == io.EOF {
|
||||||
|
return fields, fileName, fileData, nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := io.ReadAll(part)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if part.FileName() != "" {
|
||||||
|
fileName = part.FileName()
|
||||||
|
fileData = data
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fields[part.FormName()] = string(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
+132
-22
@@ -1,5 +1,7 @@
|
|||||||
package tgapi
|
package tgapi
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
// UploadPhotoP holds parameters for uploading a photo using the Uploader.
|
// UploadPhotoP holds parameters for uploading a photo using the Uploader.
|
||||||
// See https://core.telegram.org/bots/api#sendphoto
|
// See https://core.telegram.org/bots/api#sendphoto
|
||||||
type UploadPhotoP struct {
|
type UploadPhotoP struct {
|
||||||
@@ -24,14 +26,24 @@ type UploadPhotoP struct {
|
|||||||
ReplyMarkup *ReplyMarkup `json:"reply_markup,omitempty"`
|
ReplyMarkup *ReplyMarkup `json:"reply_markup,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// UploadPhoto uploads a photo and sends it as a message.
|
// SendPhoto uploads a photo via multipart and sends it as a message.
|
||||||
// file is the photo file to upload.
|
// file is the photo file to upload.
|
||||||
// See https://core.telegram.org/bots/api#sendphoto
|
// See https://core.telegram.org/bots/api#sendphoto
|
||||||
func (u *Uploader) UploadPhoto(params UploadPhotoP, file UploaderFile) (Message, error) {
|
func (u *Uploader) SendPhoto(params UploadPhotoP, file UploaderFile) (Message, error) {
|
||||||
req := NewUploaderRequestWithChatID[Message]("sendPhoto", params, params.ChatID, file)
|
req := NewUploaderRequestWithChatID[Message]("sendPhoto", params, params.ChatID, file)
|
||||||
return req.Do(u)
|
return req.Do(u)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SendPhotoWithContext is the context-aware variant of SendPhoto.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// SendPhotoWithContext is the context-aware variant of SendPhoto.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#sendphoto
|
||||||
|
func (u *Uploader) SendPhotoWithContext(ctx context.Context, params UploadPhotoP, file UploaderFile) (Message, error) {
|
||||||
|
req := NewUploaderRequestWithChatID[Message]("sendPhoto", params, params.ChatID, file)
|
||||||
|
return req.DoWithContext(ctx, u)
|
||||||
|
}
|
||||||
|
|
||||||
// UploadAudioP holds parameters for uploading an audio file using the Uploader.
|
// UploadAudioP holds parameters for uploading an audio file using the Uploader.
|
||||||
// See https://core.telegram.org/bots/api#sendaudio
|
// See https://core.telegram.org/bots/api#sendaudio
|
||||||
type UploadAudioP struct {
|
type UploadAudioP struct {
|
||||||
@@ -58,14 +70,24 @@ type UploadAudioP struct {
|
|||||||
ReplyMarkup *ReplyMarkup `json:"reply_markup,omitempty"`
|
ReplyMarkup *ReplyMarkup `json:"reply_markup,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// UploadAudio uploads an audio file and sends it as a message.
|
// SendAudio uploads an audio file via multipart and sends it as a message.
|
||||||
// files are the audio file(s) to upload (typically one file).
|
// files are the audio file(s) to upload (typically one file).
|
||||||
// See https://core.telegram.org/bots/api#sendaudio
|
// See https://core.telegram.org/bots/api#sendaudio
|
||||||
func (u *Uploader) UploadAudio(params UploadAudioP, files ...UploaderFile) (Message, error) {
|
func (u *Uploader) SendAudio(params UploadAudioP, files ...UploaderFile) (Message, error) {
|
||||||
req := NewUploaderRequestWithChatID[Message]("sendAudio", params, params.ChatID, files...)
|
req := NewUploaderRequestWithChatID[Message]("sendAudio", params, params.ChatID, files...)
|
||||||
return req.Do(u)
|
return req.Do(u)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SendAudioWithContext is the context-aware variant of SendAudio.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// SendAudioWithContext is the context-aware variant of SendAudio.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#sendaudio
|
||||||
|
func (u *Uploader) SendAudioWithContext(ctx context.Context, params UploadAudioP, files ...UploaderFile) (Message, error) {
|
||||||
|
req := NewUploaderRequestWithChatID[Message]("sendAudio", params, params.ChatID, files...)
|
||||||
|
return req.DoWithContext(ctx, u)
|
||||||
|
}
|
||||||
|
|
||||||
// UploadDocumentP holds parameters for uploading a document using the Uploader.
|
// UploadDocumentP holds parameters for uploading a document using the Uploader.
|
||||||
// See https://core.telegram.org/bots/api#senddocument
|
// See https://core.telegram.org/bots/api#senddocument
|
||||||
type UploadDocumentP struct {
|
type UploadDocumentP struct {
|
||||||
@@ -89,14 +111,24 @@ type UploadDocumentP struct {
|
|||||||
ReplyMarkup *ReplyMarkup `json:"reply_markup,omitempty"`
|
ReplyMarkup *ReplyMarkup `json:"reply_markup,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// UploadDocument uploads a document and sends it as a message.
|
// SendDocument uploads a document via multipart and sends it as a message.
|
||||||
// files are the document file(s) to upload (typically one file).
|
// files are the document file(s) to upload (typically one file).
|
||||||
// See https://core.telegram.org/bots/api#senddocument
|
// See https://core.telegram.org/bots/api#senddocument
|
||||||
func (u *Uploader) UploadDocument(params UploadDocumentP, files ...UploaderFile) (Message, error) {
|
func (u *Uploader) SendDocument(params UploadDocumentP, files ...UploaderFile) (Message, error) {
|
||||||
req := NewUploaderRequest[Message]("sendDocument", params, files...)
|
req := NewUploaderRequestWithChatID[Message]("sendDocument", params, params.ChatID, files...)
|
||||||
return req.Do(u)
|
return req.Do(u)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SendDocumentWithContext is the context-aware variant of SendDocument.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// SendDocumentWithContext is the context-aware variant of SendDocument.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#senddocument
|
||||||
|
func (u *Uploader) SendDocumentWithContext(ctx context.Context, params UploadDocumentP, files ...UploaderFile) (Message, error) {
|
||||||
|
req := NewUploaderRequestWithChatID[Message]("sendDocument", params, params.ChatID, files...)
|
||||||
|
return req.DoWithContext(ctx, u)
|
||||||
|
}
|
||||||
|
|
||||||
// UploadVideoP holds parameters for uploading a video using the Uploader.
|
// UploadVideoP holds parameters for uploading a video using the Uploader.
|
||||||
// See https://core.telegram.org/bots/api#sendvideo
|
// See https://core.telegram.org/bots/api#sendvideo
|
||||||
type UploadVideoP struct {
|
type UploadVideoP struct {
|
||||||
@@ -127,14 +159,24 @@ type UploadVideoP struct {
|
|||||||
ReplyMarkup *ReplyMarkup `json:"reply_markup,omitempty"`
|
ReplyMarkup *ReplyMarkup `json:"reply_markup,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// UploadVideo uploads a video and sends it as a message.
|
// SendVideo uploads a video via multipart and sends it as a message.
|
||||||
// files are the video file(s) to upload (typically one file).
|
// files are the video file(s) to upload (typically one file).
|
||||||
// See https://core.telegram.org/bots/api#sendvideo
|
// See https://core.telegram.org/bots/api#sendvideo
|
||||||
func (u *Uploader) UploadVideo(params UploadVideoP, files ...UploaderFile) (Message, error) {
|
func (u *Uploader) SendVideo(params UploadVideoP, files ...UploaderFile) (Message, error) {
|
||||||
req := NewUploaderRequest[Message]("sendVideo", params, files...)
|
req := NewUploaderRequestWithChatID[Message]("sendVideo", params, params.ChatID, files...)
|
||||||
return req.Do(u)
|
return req.Do(u)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SendVideoWithContext is the context-aware variant of SendVideo.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// SendVideoWithContext is the context-aware variant of SendVideo.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#sendvideo
|
||||||
|
func (u *Uploader) SendVideoWithContext(ctx context.Context, params UploadVideoP, files ...UploaderFile) (Message, error) {
|
||||||
|
req := NewUploaderRequestWithChatID[Message]("sendVideo", params, params.ChatID, files...)
|
||||||
|
return req.DoWithContext(ctx, u)
|
||||||
|
}
|
||||||
|
|
||||||
// UploadAnimationP holds parameters for uploading an animation using the Uploader.
|
// UploadAnimationP holds parameters for uploading an animation using the Uploader.
|
||||||
// See https://core.telegram.org/bots/api#sendanimation
|
// See https://core.telegram.org/bots/api#sendanimation
|
||||||
type UploadAnimationP struct {
|
type UploadAnimationP struct {
|
||||||
@@ -163,14 +205,24 @@ type UploadAnimationP struct {
|
|||||||
ReplyMarkup *ReplyMarkup `json:"reply_markup,omitempty"`
|
ReplyMarkup *ReplyMarkup `json:"reply_markup,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// UploadAnimation uploads an animation (GIF or H.264/MPEG-4 AVC video without sound) and sends it as a message.
|
// SendAnimation uploads an animation via multipart and sends it as a message.
|
||||||
// files are the animation file(s) to upload (typically one file).
|
// files are the animation file(s) to upload (typically one file).
|
||||||
// See https://core.telegram.org/bots/api#sendanimation
|
// See https://core.telegram.org/bots/api#sendanimation
|
||||||
func (u *Uploader) UploadAnimation(params UploadAnimationP, files ...UploaderFile) (Message, error) {
|
func (u *Uploader) SendAnimation(params UploadAnimationP, files ...UploaderFile) (Message, error) {
|
||||||
req := NewUploaderRequest[Message]("sendAnimation", params, files...)
|
req := NewUploaderRequestWithChatID[Message]("sendAnimation", params, params.ChatID, files...)
|
||||||
return req.Do(u)
|
return req.Do(u)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SendAnimationWithContext is the context-aware variant of SendAnimation.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// SendAnimationWithContext is the context-aware variant of SendAnimation.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#sendanimation
|
||||||
|
func (u *Uploader) SendAnimationWithContext(ctx context.Context, params UploadAnimationP, files ...UploaderFile) (Message, error) {
|
||||||
|
req := NewUploaderRequestWithChatID[Message]("sendAnimation", params, params.ChatID, files...)
|
||||||
|
return req.DoWithContext(ctx, u)
|
||||||
|
}
|
||||||
|
|
||||||
// UploadVoiceP holds parameters for uploading a voice note using the Uploader.
|
// UploadVoiceP holds parameters for uploading a voice note using the Uploader.
|
||||||
// See https://core.telegram.org/bots/api#sendvoice
|
// See https://core.telegram.org/bots/api#sendvoice
|
||||||
type UploadVoiceP struct {
|
type UploadVoiceP struct {
|
||||||
@@ -194,14 +246,24 @@ type UploadVoiceP struct {
|
|||||||
ReplyMarkup *ReplyMarkup `json:"reply_markup,omitempty"`
|
ReplyMarkup *ReplyMarkup `json:"reply_markup,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// UploadVoice uploads a voice note and sends it as a message.
|
// SendVoice uploads a voice note via multipart and sends it as a message.
|
||||||
// files are the voice file(s) to upload (typically one file).
|
// files are the voice file(s) to upload (typically one file).
|
||||||
// See https://core.telegram.org/bots/api#sendvoice
|
// See https://core.telegram.org/bots/api#sendvoice
|
||||||
func (u *Uploader) UploadVoice(params UploadVoiceP, files ...UploaderFile) (Message, error) {
|
func (u *Uploader) SendVoice(params UploadVoiceP, files ...UploaderFile) (Message, error) {
|
||||||
req := NewUploaderRequest[Message]("sendVoice", params, files...)
|
req := NewUploaderRequestWithChatID[Message]("sendVoice", params, params.ChatID, files...)
|
||||||
return req.Do(u)
|
return req.Do(u)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SendVoiceWithContext is the context-aware variant of SendVoice.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// SendVoiceWithContext is the context-aware variant of SendVoice.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#sendvoice
|
||||||
|
func (u *Uploader) SendVoiceWithContext(ctx context.Context, params UploadVoiceP, files ...UploaderFile) (Message, error) {
|
||||||
|
req := NewUploaderRequestWithChatID[Message]("sendVoice", params, params.ChatID, files...)
|
||||||
|
return req.DoWithContext(ctx, u)
|
||||||
|
}
|
||||||
|
|
||||||
// UploadVideoNoteP holds parameters for uploading a video note (rounded video) using the Uploader.
|
// UploadVideoNoteP holds parameters for uploading a video note (rounded video) using the Uploader.
|
||||||
// See https://core.telegram.org/bots/api#sendvideonote
|
// See https://core.telegram.org/bots/api#sendvideonote
|
||||||
type UploadVideoNoteP struct {
|
type UploadVideoNoteP struct {
|
||||||
@@ -223,24 +285,72 @@ type UploadVideoNoteP struct {
|
|||||||
ReplyMarkup *ReplyMarkup `json:"reply_markup,omitempty"`
|
ReplyMarkup *ReplyMarkup `json:"reply_markup,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// UploadVideoNote uploads a video note (rounded video) and sends it as a message.
|
// SendVideoNote uploads a video note via multipart and sends it as a message.
|
||||||
// files are the video note file(s) to upload (typically one file).
|
// files are the video note file(s) to upload (typically one file).
|
||||||
// See https://core.telegram.org/bots/api#sendvideonote
|
// See https://core.telegram.org/bots/api#sendvideonote
|
||||||
func (u *Uploader) UploadVideoNote(params UploadVideoNoteP, files ...UploaderFile) (Message, error) {
|
func (u *Uploader) SendVideoNote(params UploadVideoNoteP, files ...UploaderFile) (Message, error) {
|
||||||
req := NewUploaderRequest[Message]("sendVideoNote", params, files...)
|
req := NewUploaderRequestWithChatID[Message]("sendVideoNote", params, params.ChatID, files...)
|
||||||
return req.Do(u)
|
return req.Do(u)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SendVideoNoteWithContext is the context-aware variant of SendVideoNote.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// SendVideoNoteWithContext is the context-aware variant of SendVideoNote.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#sendvideonote
|
||||||
|
func (u *Uploader) SendVideoNoteWithContext(ctx context.Context, params UploadVideoNoteP, files ...UploaderFile) (Message, error) {
|
||||||
|
req := NewUploaderRequestWithChatID[Message]("sendVideoNote", params, params.ChatID, files...)
|
||||||
|
return req.DoWithContext(ctx, u)
|
||||||
|
}
|
||||||
|
|
||||||
// UploadChatPhotoP holds parameters for uploading a chat photo using the Uploader.
|
// UploadChatPhotoP holds parameters for uploading a chat photo using the Uploader.
|
||||||
// See https://core.telegram.org/bots/api#setchatphoto
|
// See https://core.telegram.org/bots/api#setchatphoto
|
||||||
type UploadChatPhotoP struct {
|
type UploadChatPhotoP struct {
|
||||||
ChatID int64 `json:"chat_id"`
|
ChatID int64 `json:"chat_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// UploadChatPhoto uploads a new chat photo.
|
// SetChatPhoto uploads a new chat photo.
|
||||||
// photo is the photo file to upload.
|
// photo is the photo file to upload.
|
||||||
// See https://core.telegram.org/bots/api#setchatphoto
|
// See https://core.telegram.org/bots/api#setchatphoto
|
||||||
func (u *Uploader) UploadChatPhoto(params UploadChatPhotoP, photo UploaderFile) (Message, error) {
|
func (u *Uploader) SetChatPhoto(params UploadChatPhotoP, photo UploaderFile) (bool, error) {
|
||||||
req := NewUploaderRequest[Message]("sendChatPhoto", params, photo)
|
req := NewUploaderRequestWithChatID[bool]("setChatPhoto", params, params.ChatID, photo)
|
||||||
return req.Do(u)
|
return req.Do(u)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetChatPhotoWithContext is the context-aware variant of SetChatPhoto.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// SetChatPhotoWithContext is the context-aware variant of SetChatPhoto.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#setchatphoto
|
||||||
|
func (u *Uploader) SetChatPhotoWithContext(ctx context.Context, params UploadChatPhotoP, photo UploaderFile) (bool, error) {
|
||||||
|
req := NewUploaderRequestWithChatID[bool]("setChatPhoto", params, params.ChatID, photo)
|
||||||
|
return req.DoWithContext(ctx, u)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UploadSetWebhookP holds multipart parameters for the setWebhook method.
|
||||||
|
// Use this type when uploading a self-signed certificate file.
|
||||||
|
// See https://core.telegram.org/bots/api#setwebhook
|
||||||
|
type UploadSetWebhookP struct {
|
||||||
|
URL string `json:"url"`
|
||||||
|
IPAddress string `json:"ip_address,omitempty"`
|
||||||
|
MaxConnections int `json:"max_connections,omitempty"`
|
||||||
|
AllowedUpdates []UpdateType `json:"allowed_updates,omitempty"`
|
||||||
|
DropPendingUpdates bool `json:"drop_pending_updates,omitempty"`
|
||||||
|
SecretToken string `json:"secret_token,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetWebhook uploads a certificate and sets a webhook URL.
|
||||||
|
// certificate maps to the multipart field \"certificate\".
|
||||||
|
// See https://core.telegram.org/bots/api#setwebhook
|
||||||
|
func (u *Uploader) SetWebhook(params UploadSetWebhookP, certificate UploaderFile) (bool, error) {
|
||||||
|
req := NewUploaderRequest[bool]("setWebhook", params, certificate.SetType(UploaderCertificateType))
|
||||||
|
return req.Do(u)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetWebhookWithContext is the context-aware variant of SetWebhook.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#setwebhook
|
||||||
|
func (u *Uploader) SetWebhookWithContext(ctx context.Context, params UploadSetWebhookP, certificate UploaderFile) (bool, error) {
|
||||||
|
req := NewUploaderRequest[bool]("setWebhook", params, certificate.SetType(UploaderCertificateType))
|
||||||
|
return req.DoWithContext(ctx, u)
|
||||||
|
}
|
||||||
|
|||||||
+42
-8
@@ -1,11 +1,13 @@
|
|||||||
package tgapi
|
package tgapi
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
// GetUserProfilePhotosP holds parameters for the GetUserProfilePhotos method.
|
// GetUserProfilePhotosP holds parameters for the GetUserProfilePhotos method.
|
||||||
// See https://core.telegram.org/bots/api#getuserprofilephotos
|
// See https://core.telegram.org/bots/api#getuserprofilephotos
|
||||||
type GetUserProfilePhotosP struct {
|
type GetUserProfilePhotosP struct {
|
||||||
UserID int `json:"user_id"`
|
UserID int64 `json:"user_id"`
|
||||||
Offset int `json:"offset,omitempty"`
|
Offset int `json:"offset,omitempty"`
|
||||||
Limit int `json:"limit,omitempty"`
|
Limit int `json:"limit,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUserProfilePhotos returns a list of profile pictures for a user.
|
// GetUserProfilePhotos returns a list of profile pictures for a user.
|
||||||
@@ -15,12 +17,20 @@ func (api *API) GetUserProfilePhotos(params GetUserProfilePhotosP) (UserProfileP
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetUserProfilePhotosWithContext is the context-aware variant of GetUserProfilePhotos.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#getuserprofilephotos
|
||||||
|
func (api *API) GetUserProfilePhotosWithContext(ctx context.Context, params GetUserProfilePhotosP) (UserProfilePhotos, error) {
|
||||||
|
req := NewRequest[UserProfilePhotos]("getUserProfilePhotos", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// GetUserProfileAudiosP holds parameters for the GetUserProfileAudios method.
|
// GetUserProfileAudiosP holds parameters for the GetUserProfileAudios method.
|
||||||
// See https://core.telegram.org/bots/api#getuserprofileaudios
|
// See https://core.telegram.org/bots/api#getuserprofileaudios
|
||||||
type GetUserProfileAudiosP struct {
|
type GetUserProfileAudiosP struct {
|
||||||
UserID int `json:"user_id"`
|
UserID int64 `json:"user_id"`
|
||||||
Offset int `json:"offset,omitempty"`
|
Offset int `json:"offset,omitempty"`
|
||||||
Limit int `json:"limit,omitempty"`
|
Limit int `json:"limit,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUserProfileAudios returns a list of profile audios for a user.
|
// GetUserProfileAudios returns a list of profile audios for a user.
|
||||||
@@ -30,10 +40,18 @@ func (api *API) GetUserProfileAudios(params GetUserProfileAudiosP) (UserProfileA
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetUserProfileAudiosWithContext is the context-aware variant of GetUserProfileAudios.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#getuserprofileaudios
|
||||||
|
func (api *API) GetUserProfileAudiosWithContext(ctx context.Context, params GetUserProfileAudiosP) (UserProfileAudios, error) {
|
||||||
|
req := NewRequest[UserProfileAudios]("getUserProfileAudios", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// SetUserEmojiStatusP holds parameters for the SetUserEmojiStatus method.
|
// SetUserEmojiStatusP holds parameters for the SetUserEmojiStatus method.
|
||||||
// See https://core.telegram.org/bots/api#setuseremojistatus
|
// See https://core.telegram.org/bots/api#setuseremojistatus
|
||||||
type SetUserEmojiStatusP struct {
|
type SetUserEmojiStatusP struct {
|
||||||
UserID int `json:"user_id"`
|
UserID int64 `json:"user_id"`
|
||||||
EmojiID string `json:"emoji_status_custom_emoji_id,omitempty"`
|
EmojiID string `json:"emoji_status_custom_emoji_id,omitempty"`
|
||||||
ExpirationDate int `json:"emoji_status_expiration_date,omitempty"`
|
ExpirationDate int `json:"emoji_status_expiration_date,omitempty"`
|
||||||
}
|
}
|
||||||
@@ -46,10 +64,18 @@ func (api *API) SetUserEmojiStatus(params SetUserEmojiStatusP) (bool, error) {
|
|||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetUserEmojiStatusWithContext is the context-aware variant of SetUserEmojiStatus.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#setuseremojistatus
|
||||||
|
func (api *API) SetUserEmojiStatusWithContext(ctx context.Context, params SetUserEmojiStatusP) (bool, error) {
|
||||||
|
req := NewRequest[bool]("setUserEmojiStatus", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|
||||||
// GetUserGiftsP holds parameters for the GetUserGifts method.
|
// GetUserGiftsP holds parameters for the GetUserGifts method.
|
||||||
// See https://core.telegram.org/bots/api#getusergifts
|
// See https://core.telegram.org/bots/api#getusergifts
|
||||||
type GetUserGiftsP struct {
|
type GetUserGiftsP struct {
|
||||||
UserID int `json:"user_id"`
|
UserID int64 `json:"user_id"`
|
||||||
ExcludeUnlimited bool `json:"exclude_unlimited,omitempty"`
|
ExcludeUnlimited bool `json:"exclude_unlimited,omitempty"`
|
||||||
ExcludeLimitedUpgradable bool `json:"exclude_limited_upgradable,omitempty"`
|
ExcludeLimitedUpgradable bool `json:"exclude_limited_upgradable,omitempty"`
|
||||||
ExcludeLimitedNonUpgradable bool `json:"exclude_limited_non_upgradable,omitempty"`
|
ExcludeLimitedNonUpgradable bool `json:"exclude_limited_non_upgradable,omitempty"`
|
||||||
@@ -66,3 +92,11 @@ func (api *API) GetUserGifts(params GetUserGiftsP) (OwnedGifts, error) {
|
|||||||
req := NewRequest[OwnedGifts]("getUserGifts", params)
|
req := NewRequest[OwnedGifts]("getUserGifts", params)
|
||||||
return req.Do(api)
|
return req.Do(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetUserGiftsWithContext is the context-aware variant of GetUserGifts.
|
||||||
|
// It executes the same request but uses ctx for cancellation and deadlines.
|
||||||
|
// See https://core.telegram.org/bots/api#getusergifts
|
||||||
|
func (api *API) GetUserGiftsWithContext(ctx context.Context, params GetUserGiftsP) (OwnedGifts, error) {
|
||||||
|
req := NewRequest[OwnedGifts]("getUserGifts", params)
|
||||||
|
return req.DoWithContext(ctx, api)
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package tgapi
|
|||||||
// User represents a Telegram user or bot.
|
// User represents a Telegram user or bot.
|
||||||
// See https://core.telegram.org/bots/api#user
|
// See https://core.telegram.org/bots/api#user
|
||||||
type User struct {
|
type User struct {
|
||||||
ID int `json:"id"`
|
ID int64 `json:"id"`
|
||||||
IsBot bool `json:"is_bot"`
|
IsBot bool `json:"is_bot"`
|
||||||
FirstName string `json:"first_name"`
|
FirstName string `json:"first_name"`
|
||||||
LastName *string `json:"last_name,omitempty"`
|
LastName *string `json:"last_name,omitempty"`
|
||||||
|
|||||||
@@ -6,7 +6,10 @@ import (
|
|||||||
"git.nix13.pw/scuroneko/laniakea/utils"
|
"git.nix13.pw/scuroneko/laniakea/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Ptr returns a pointer to v.
|
||||||
func Ptr[T any](v T) *T { return &v }
|
func Ptr[T any](v T) *T { return &v }
|
||||||
|
|
||||||
|
// Val returns dereferenced pointer value or def when p is nil.
|
||||||
func Val[T any](p *T, def T) T {
|
func Val[T any](p *T, def T) T {
|
||||||
if p != nil {
|
if p != nil {
|
||||||
return *p
|
return *p
|
||||||
@@ -14,8 +17,8 @@ func Val[T any](p *T, def T) T {
|
|||||||
return def
|
return def
|
||||||
}
|
}
|
||||||
|
|
||||||
// EscapeMarkdown
|
// EscapeMarkdown escapes special characters for legacy Telegram Markdown.
|
||||||
// Deprecated. Use MarkdownV2
|
// Deprecated: Use EscapeMarkdownV2.
|
||||||
func EscapeMarkdown(s string) string {
|
func EscapeMarkdown(s string) string {
|
||||||
s = strings.ReplaceAll(s, "_", `\_`)
|
s = strings.ReplaceAll(s, "_", `\_`)
|
||||||
s = strings.ReplaceAll(s, "*", `\*`)
|
s = strings.ReplaceAll(s, "*", `\*`)
|
||||||
@@ -40,6 +43,8 @@ func EscapeMarkdownV2(s string) string {
|
|||||||
}
|
}
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EscapePunctuation escapes '.', '!' and '-' for MarkdownV2 fragments.
|
||||||
func EscapePunctuation(s string) string {
|
func EscapePunctuation(s string) string {
|
||||||
symbols := []string{".", "!", "-"}
|
symbols := []string{".", "!", "-"}
|
||||||
for _, symbol := range symbols {
|
for _, symbol := range symbols {
|
||||||
@@ -48,6 +53,7 @@ func EscapePunctuation(s string) string {
|
|||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Version constants mirror values from the internal utils/version package.
|
||||||
const (
|
const (
|
||||||
VersionString = utils.VersionString
|
VersionString = utils.VersionString
|
||||||
VersionMajor = utils.VersionMajor
|
VersionMajor = utils.VersionMajor
|
||||||
|
|||||||
+54
-17
@@ -22,7 +22,7 @@ type RateLimiter struct {
|
|||||||
|
|
||||||
chatLocks map[int64]time.Time // per-chat cooldown timestamps
|
chatLocks map[int64]time.Time // per-chat cooldown timestamps
|
||||||
chatLimiters map[int64]*rate.Limiter // per-chat token buckets (1 req/sec)
|
chatLimiters map[int64]*rate.Limiter // per-chat token buckets (1 req/sec)
|
||||||
chatMu sync.Mutex // protects chatLocks and chatLimiters
|
chatMu sync.RWMutex // protects chatLocks and chatLimiters
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRateLimiter creates a new RateLimiter with default limits.
|
// NewRateLimiter creates a new RateLimiter with default limits.
|
||||||
@@ -36,6 +36,17 @@ func NewRateLimiter() *RateLimiter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetGlobalRate overrides global request-per-second limit and burst.
|
||||||
|
// If rps <= 0, current settings are kept.
|
||||||
|
func (rl *RateLimiter) SetGlobalRate(rps int) {
|
||||||
|
if rps <= 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
rl.globalMu.Lock()
|
||||||
|
defer rl.globalMu.Unlock()
|
||||||
|
rl.globalLimiter = rate.NewLimiter(rate.Limit(rps), rps)
|
||||||
|
}
|
||||||
|
|
||||||
// SetGlobalLock sets a global cooldown period (e.g., after receiving 429 from Telegram).
|
// SetGlobalLock sets a global cooldown period (e.g., after receiving 429 from Telegram).
|
||||||
// If retryAfter <= 0, no lock is applied.
|
// If retryAfter <= 0, no lock is applied.
|
||||||
func (rl *RateLimiter) SetGlobalLock(retryAfter int) {
|
func (rl *RateLimiter) SetGlobalLock(retryAfter int) {
|
||||||
@@ -64,7 +75,11 @@ func (rl *RateLimiter) GlobalWait(ctx context.Context) error {
|
|||||||
if err := rl.waitForGlobalUnlock(ctx); err != nil {
|
if err := rl.waitForGlobalUnlock(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return rl.globalLimiter.Wait(ctx)
|
limiter := rl.getGlobalLimiter()
|
||||||
|
if limiter == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return limiter.Wait(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait blocks until a request for the given chat can be made.
|
// Wait blocks until a request for the given chat can be made.
|
||||||
@@ -77,8 +92,21 @@ func (rl *RateLimiter) Wait(ctx context.Context, chatID int64) error {
|
|||||||
if err := rl.waitForGlobalUnlock(ctx); err != nil {
|
if err := rl.waitForGlobalUnlock(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
limiter := rl.getChatLimiter(chatID)
|
limiter := rl.getGlobalLimiter()
|
||||||
return limiter.Wait(ctx)
|
if limiter != nil {
|
||||||
|
if err := limiter.Wait(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
chatLimiter := rl.getChatLimiter(chatID)
|
||||||
|
return chatLimiter.Wait(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getGlobalLimiter returns the global limiter safely under read lock.
|
||||||
|
func (rl *RateLimiter) getGlobalLimiter() *rate.Limiter {
|
||||||
|
rl.globalMu.RLock()
|
||||||
|
defer rl.globalMu.RUnlock()
|
||||||
|
return rl.globalLimiter
|
||||||
}
|
}
|
||||||
|
|
||||||
// GlobalAllow checks if a global request can be made without blocking.
|
// GlobalAllow checks if a global request can be made without blocking.
|
||||||
@@ -91,7 +119,11 @@ func (rl *RateLimiter) GlobalAllow() bool {
|
|||||||
if !until.IsZero() && time.Now().Before(until) {
|
if !until.IsZero() && time.Now().Before(until) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return rl.globalLimiter.Allow()
|
limiter := rl.getGlobalLimiter()
|
||||||
|
if limiter == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return limiter.Allow()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allow checks if a request for the given chat can be made without blocking.
|
// Allow checks if a request for the given chat can be made without blocking.
|
||||||
@@ -107,21 +139,22 @@ func (rl *RateLimiter) Allow(chatID int64) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check chat cooldown
|
// Check chat cooldown
|
||||||
rl.chatMu.Lock()
|
rl.chatMu.RLock()
|
||||||
chatUntil, ok := rl.chatLocks[chatID]
|
chatUntil, ok := rl.chatLocks[chatID]
|
||||||
rl.chatMu.Unlock()
|
rl.chatMu.RUnlock()
|
||||||
if ok && !chatUntil.IsZero() && time.Now().Before(chatUntil) {
|
if ok && !chatUntil.IsZero() && time.Now().Before(chatUntil) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check global token bucket
|
// Check global token bucket
|
||||||
if !rl.globalLimiter.Allow() {
|
limiter := rl.getGlobalLimiter()
|
||||||
|
if limiter != nil && !limiter.Allow() {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check chat token bucket
|
// Check chat token bucket
|
||||||
limiter := rl.getChatLimiter(chatID)
|
chatLimiter := rl.getChatLimiter(chatID)
|
||||||
return limiter.Allow()
|
return chatLimiter.Allow()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check applies rate limiting based on configuration.
|
// Check applies rate limiting based on configuration.
|
||||||
@@ -135,11 +168,15 @@ func (rl *RateLimiter) Allow(chatID int64) bool {
|
|||||||
// chatID == 0 means no specific chat context (e.g., inline query, webhook without chat).
|
// chatID == 0 means no specific chat context (e.g., inline query, webhook without chat).
|
||||||
func (rl *RateLimiter) Check(ctx context.Context, dropOverflow bool, chatID int64) error {
|
func (rl *RateLimiter) Check(ctx context.Context, dropOverflow bool, chatID int64) error {
|
||||||
if dropOverflow {
|
if dropOverflow {
|
||||||
if chatID != 0 && !rl.Allow(chatID) {
|
if chatID != 0 {
|
||||||
return ErrDropOverflow
|
if !rl.Allow(chatID) {
|
||||||
}
|
|
||||||
if !rl.GlobalAllow() {
|
return ErrDropOverflow
|
||||||
return ErrDropOverflow
|
}
|
||||||
|
} else {
|
||||||
|
if !rl.GlobalAllow() {
|
||||||
|
return ErrDropOverflow
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if chatID != 0 {
|
} else if chatID != 0 {
|
||||||
if err := rl.Wait(ctx, chatID); err != nil {
|
if err := rl.Wait(ctx, chatID); err != nil {
|
||||||
@@ -175,9 +212,9 @@ func (rl *RateLimiter) waitForGlobalUnlock(ctx context.Context) error {
|
|||||||
// waitForChatUnlock blocks until the specified chat's cooldown expires or context is done.
|
// waitForChatUnlock blocks until the specified chat's cooldown expires or context is done.
|
||||||
// Does not check token bucket — only cooldown.
|
// Does not check token bucket — only cooldown.
|
||||||
func (rl *RateLimiter) waitForChatUnlock(ctx context.Context, chatID int64) error {
|
func (rl *RateLimiter) waitForChatUnlock(ctx context.Context, chatID int64) error {
|
||||||
rl.chatMu.Lock()
|
rl.chatMu.RLock()
|
||||||
until, ok := rl.chatLocks[chatID]
|
until, ok := rl.chatLocks[chatID]
|
||||||
rl.chatMu.Unlock()
|
rl.chatMu.RUnlock()
|
||||||
|
|
||||||
if !ok || until.IsZero() || time.Now().After(until) {
|
if !ok || until.IsZero() || time.Now().After(until) {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
+71
-85
@@ -1,6 +1,7 @@
|
|||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"mime/multipart"
|
"mime/multipart"
|
||||||
@@ -10,13 +11,10 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Encode writes struct fields into multipart form-data using json tags as field names.
|
||||||
func Encode[T any](w *multipart.Writer, req T) error {
|
func Encode[T any](w *multipart.Writer, req T) error {
|
||||||
v := reflect.ValueOf(req)
|
v := unwrapMultipartValue(reflect.ValueOf(req))
|
||||||
if v.Kind() == reflect.Ptr {
|
if !v.IsValid() || v.Kind() != reflect.Struct {
|
||||||
v = v.Elem()
|
|
||||||
}
|
|
||||||
|
|
||||||
if v.Kind() != reflect.Struct {
|
|
||||||
return fmt.Errorf("req must be a struct")
|
return fmt.Errorf("req must be a struct")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,6 +30,9 @@ func Encode[T any](w *multipart.Writer, req T) error {
|
|||||||
|
|
||||||
parts := strings.Split(jsonTag, ",")
|
parts := strings.Split(jsonTag, ",")
|
||||||
fieldName := parts[0]
|
fieldName := parts[0]
|
||||||
|
if fieldName == "" {
|
||||||
|
fieldName = fieldType.Name
|
||||||
|
}
|
||||||
if fieldName == "-" {
|
if fieldName == "-" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -42,88 +43,73 @@ func Encode[T any](w *multipart.Writer, req T) error {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
if err := writeMultipartField(w, fieldName, fieldType.Tag.Get("filename"), field); err != nil {
|
||||||
fw io.Writer
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
|
|
||||||
switch field.Kind() {
|
|
||||||
case reflect.String:
|
|
||||||
if !isEmpty {
|
|
||||||
fw, err = w.CreateFormField(fieldName)
|
|
||||||
if err == nil {
|
|
||||||
_, err = fw.Write([]byte(field.String()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
||||||
fw, err = w.CreateFormField(fieldName)
|
|
||||||
if err == nil {
|
|
||||||
_, err = fw.Write([]byte(strconv.FormatInt(field.Int(), 10)))
|
|
||||||
}
|
|
||||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
|
||||||
fw, err = w.CreateFormField(fieldName)
|
|
||||||
if err == nil {
|
|
||||||
_, err = fw.Write([]byte(strconv.FormatUint(field.Uint(), 10)))
|
|
||||||
}
|
|
||||||
case reflect.Float32, reflect.Float64:
|
|
||||||
fw, err = w.CreateFormField(fieldName)
|
|
||||||
if err == nil {
|
|
||||||
_, err = fw.Write([]byte(strconv.FormatFloat(field.Float(), 'f', -1, 64)))
|
|
||||||
}
|
|
||||||
case reflect.Bool:
|
|
||||||
fw, err = w.CreateFormField(fieldName)
|
|
||||||
if err == nil {
|
|
||||||
_, err = fw.Write([]byte(strconv.FormatBool(field.Bool())))
|
|
||||||
}
|
|
||||||
case reflect.Slice:
|
|
||||||
if field.Type().Elem().Kind() == reflect.Uint8 && !field.IsNil() {
|
|
||||||
// Handle []byte as file upload (e.g., thumbnail)
|
|
||||||
filename := fieldType.Tag.Get("filename")
|
|
||||||
if filename == "" {
|
|
||||||
filename = fieldName
|
|
||||||
}
|
|
||||||
fw, err = w.CreateFormFile(fieldName, filename)
|
|
||||||
if err == nil {
|
|
||||||
_, err = fw.Write(field.Bytes())
|
|
||||||
}
|
|
||||||
} else if !field.IsNil() {
|
|
||||||
// Handle []string, []int, etc. — send as multiple fields with same name
|
|
||||||
for j := 0; j < field.Len(); j++ {
|
|
||||||
elem := field.Index(j)
|
|
||||||
fw, err = w.CreateFormField(fieldName)
|
|
||||||
if err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
switch elem.Kind() {
|
|
||||||
case reflect.String:
|
|
||||||
_, err = fw.Write([]byte(elem.String()))
|
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
||||||
_, err = fw.Write([]byte(strconv.FormatInt(elem.Int(), 10)))
|
|
||||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
|
||||||
_, err = fw.Write([]byte(strconv.FormatUint(elem.Uint(), 10)))
|
|
||||||
case reflect.Bool:
|
|
||||||
_, err = fw.Write([]byte(strconv.FormatBool(elem.Bool())))
|
|
||||||
case reflect.Float32, reflect.Float64:
|
|
||||||
_, err = fw.Write([]byte(strconv.FormatFloat(elem.Float(), 'f', -1, 64)))
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case reflect.Struct:
|
|
||||||
// Don't serialize structs as JSON — flatten them!
|
|
||||||
// Telegram doesn't support nested JSON in form-data.
|
|
||||||
// If you need nested data, use separate fields (e.g., ParseMode, CaptionEntities)
|
|
||||||
// This is a design choice — you should avoid nested structs in params.
|
|
||||||
return fmt.Errorf("nested structs are not supported in params — use flat fields")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func unwrapMultipartValue(v reflect.Value) reflect.Value {
|
||||||
|
for v.IsValid() && (v.Kind() == reflect.Pointer || v.Kind() == reflect.Interface) {
|
||||||
|
if v.IsNil() {
|
||||||
|
return reflect.Value{}
|
||||||
|
}
|
||||||
|
v = v.Elem()
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeMultipartField(w *multipart.Writer, fieldName, filename string, field reflect.Value) error {
|
||||||
|
value := unwrapMultipartValue(field)
|
||||||
|
if !value.IsValid() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
switch value.Kind() {
|
||||||
|
case reflect.String:
|
||||||
|
return writeMultipartValue(w, fieldName, []byte(value.String()))
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
return writeMultipartValue(w, fieldName, []byte(strconv.FormatInt(value.Int(), 10)))
|
||||||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||||
|
return writeMultipartValue(w, fieldName, []byte(strconv.FormatUint(value.Uint(), 10)))
|
||||||
|
case reflect.Float32:
|
||||||
|
return writeMultipartValue(w, fieldName, []byte(strconv.FormatFloat(value.Float(), 'f', -1, 32)))
|
||||||
|
case reflect.Float64:
|
||||||
|
return writeMultipartValue(w, fieldName, []byte(strconv.FormatFloat(value.Float(), 'f', -1, 64)))
|
||||||
|
case reflect.Bool:
|
||||||
|
return writeMultipartValue(w, fieldName, []byte(strconv.FormatBool(value.Bool())))
|
||||||
|
case reflect.Slice:
|
||||||
|
if value.Type().Elem().Kind() == reflect.Uint8 {
|
||||||
|
if filename == "" {
|
||||||
|
filename = fieldName
|
||||||
|
}
|
||||||
|
fw, err := w.CreateFormFile(fieldName, filename)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = fw.Write(value.Bytes())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Telegram expects nested objects and arrays in multipart requests as JSON strings.
|
||||||
|
data, err := json.Marshal(value.Interface())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if string(data) == "null" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return writeMultipartValue(w, fieldName, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeMultipartValue(w *multipart.Writer, fieldName string, value []byte) error {
|
||||||
|
fw, err := w.CreateFormField(fieldName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = io.Copy(fw, strings.NewReader(string(value)))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
package utils_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"mime/multipart"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"git.nix13.pw/scuroneko/laniakea/tgapi"
|
||||||
|
"git.nix13.pw/scuroneko/laniakea/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type multipartEncodeParams struct {
|
||||||
|
ChatID int64 `json:"chat_id"`
|
||||||
|
MessageThreadID *int `json:"message_thread_id,omitempty"`
|
||||||
|
ReplyMarkup *tgapi.ReplyMarkup `json:"reply_markup,omitempty"`
|
||||||
|
CaptionEntities []tgapi.MessageEntity `json:"caption_entities,omitempty"`
|
||||||
|
ReplyParameters *tgapi.ReplyParameters `json:"reply_parameters,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEncodeMultipartJSONFields(t *testing.T) {
|
||||||
|
threadID := 7
|
||||||
|
params := multipartEncodeParams{
|
||||||
|
ChatID: 42,
|
||||||
|
MessageThreadID: &threadID,
|
||||||
|
ReplyMarkup: &tgapi.ReplyMarkup{
|
||||||
|
InlineKeyboard: [][]tgapi.InlineKeyboardButton{{
|
||||||
|
{Text: "A", CallbackData: "b"},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
CaptionEntities: []tgapi.MessageEntity{{
|
||||||
|
Type: tgapi.MessageEntityBold,
|
||||||
|
Offset: 0,
|
||||||
|
Length: 4,
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
|
||||||
|
body := bytes.NewBuffer(nil)
|
||||||
|
writer := multipart.NewWriter(body)
|
||||||
|
if err := utils.Encode(writer, params); err != nil {
|
||||||
|
t.Fatalf("Encode returned error: %v", err)
|
||||||
|
}
|
||||||
|
if err := writer.Close(); err != nil {
|
||||||
|
t.Fatalf("writer.Close returned error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
got := readMultipartFields(t, body.Bytes(), writer.Boundary())
|
||||||
|
if got["chat_id"] != "42" {
|
||||||
|
t.Fatalf("chat_id mismatch: %q", got["chat_id"])
|
||||||
|
}
|
||||||
|
if got["message_thread_id"] != "7" {
|
||||||
|
t.Fatalf("message_thread_id mismatch: %q", got["message_thread_id"])
|
||||||
|
}
|
||||||
|
if got["reply_markup"] != `{"inline_keyboard":[[{"text":"A","callback_data":"b"}]]}` {
|
||||||
|
t.Fatalf("reply_markup mismatch: %q", got["reply_markup"])
|
||||||
|
}
|
||||||
|
if got["caption_entities"] != `[{"type":"bold","offset":0,"length":4}]` {
|
||||||
|
t.Fatalf("caption_entities mismatch: %q", got["caption_entities"])
|
||||||
|
}
|
||||||
|
if _, ok := got["reply_parameters"]; ok {
|
||||||
|
t.Fatalf("reply_parameters should be omitted when nil")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func readMultipartFields(t *testing.T, body []byte, boundary string) map[string]string {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
reader := multipart.NewReader(bytes.NewReader(body), boundary)
|
||||||
|
fields := make(map[string]string)
|
||||||
|
for {
|
||||||
|
part, err := reader.NextPart()
|
||||||
|
if err == io.EOF {
|
||||||
|
return fields
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("NextPart returned error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := io.ReadAll(part)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ReadAll returned error: %v", err)
|
||||||
|
}
|
||||||
|
fields[part.FormName()] = string(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"git.nix13.pw/scuroneko/slog"
|
"git.nix13.pw/scuroneko/slog"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// GetLoggerLevel returns DEBUG when DEBUG=true in env, otherwise FATAL.
|
||||||
func GetLoggerLevel() slog.LogLevel {
|
func GetLoggerLevel() slog.LogLevel {
|
||||||
level := slog.FATAL
|
level := slog.FATAL
|
||||||
if os.Getenv("DEBUG") == "true" {
|
if os.Getenv("DEBUG") == "true" {
|
||||||
|
|||||||
+2
-2
@@ -1,9 +1,9 @@
|
|||||||
package utils
|
package utils
|
||||||
|
|
||||||
const (
|
const (
|
||||||
VersionString = "1.0.0-beta.20"
|
VersionString = "1.0.0-rc.4"
|
||||||
VersionMajor = 1
|
VersionMajor = 1
|
||||||
VersionMinor = 0
|
VersionMinor = 0
|
||||||
VersionPatch = 0
|
VersionPatch = 0
|
||||||
VersionBeta = 20
|
VersionBeta = 4
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user