diff --git a/Auto-Generated-Commands.md b/Auto-Generated-Commands.md index 316e0a9..b5a1803 100644 --- a/Auto-Generated-Commands.md +++ b/Auto-Generated-Commands.md @@ -116,7 +116,7 @@ There are two skip levels: ### Skip a single command ```go -plugin.NewCommand(exec, "internal"). +plugin.Command("internal", exec). SetDescription("Internal only"). SkipCommandAutoGen() ``` diff --git a/Bot-Lifecycle-RU.md b/Bot-Lifecycle-RU.md index 9a663cb..dda4337 100644 --- a/Bot-Lifecycle-RU.md +++ b/Bot-Lifecycle-RU.md @@ -9,7 +9,7 @@ English version: [[Bot-Lifecycle]] 1. Собрать `BotOpts`. 2. Создать `Bot` через `NewBot[T](opts)`. 3. Полностью настроить бот: плагины, middleware, фоновые задачи, политику данных callback, l10n и app data. -4. Запустить через `Run()`, `RunWithContext(...)` или `RunWebHookWithContext(...)`. +4. Запустить через `Run()`, `RunWithContext(...)` или `RunWebhookWithContext(...)`. 5. Остановить выполнение через завершение runtime или отмену context. 6. Освободить локальные ресурсы через `Close()`. 7. Для следующего запуска создать новый `Bot`. @@ -66,7 +66,7 @@ English version: [[Bot-Lifecycle]] 1. Построение и настройка `Bot` после `NewBot[T](opts)`. 2. Снимок конфигурации плагина в `AddPlugins(...)`. -3. Фиксация bot-level конфигурации после первого `Run()`, `RunWithContext(...)` или `RunWebHookWithContext(...)`. +3. Фиксация bot-level конфигурации после первого `Run()`, `RunWithContext(...)` или `RunWebhookWithContext(...)`. Практически это значит: - структуру плагина нужно закончить до `AddPlugins(...)`; @@ -85,7 +85,7 @@ Laniakea предпочитает предсказуемый no-op вместо - путаницу в том, влияет ли изменение только на будущие update или ещё и на уже принятую работу; - разные ментальные модели для snapshot-поведения плагинов и bot-level состояния. -## `Run()`, `RunWithContext(...)` и `RunWebHookWithContext(...)` +## `Run()`, `RunWithContext(...)` и `RunWebhookWithContext(...)` `Run()` — это короткая форма для простых случаев. @@ -94,11 +94,11 @@ Laniakea предпочитает предсказуемый no-op вместо - ждет завершения queued updates; - корректно дожидается фоновых задач. -`RunWebHookWithContext(...)` — webhook-вариант runtime. Он использует тот же single-use контракт, тот же запуск runners, ту же очередь обновлений и ту же worker-pool обработку. +`RunWebhookWithContext(...)` — webhook-вариант runtime. Он использует тот же single-use контракт, тот же запуск runners, ту же очередь обновлений и ту же worker-pool обработку. Если bot уже был запущен раньше, повторный запуск вернет `ErrBotAlreadyRun`. -Если ты переводишь уже существующий deployment с webhook-доставки на polling, сначала удали текущий webhook через `CloseWebHook()` или низкоуровневый `tgapi.DeleteWebhook(...)`. Пока webhook не удалён, Telegram продолжает доставлять update через него. +Если ты переводишь уже существующий deployment с webhook-доставки на polling, сначала удали текущий webhook через `CloseWebhook()` или низкоуровневый `tgapi.DeleteWebhook(...)`. Пока webhook не удалён, Telegram продолжает доставлять update через него. Для webhook-специфичных опций, транспортного поведения и практических советов смотри [[Webhook-Runtime-RU]]. @@ -131,7 +131,7 @@ Laniakea предпочитает предсказуемый no-op вместо Обычно боту нужен именно `Close()`. -`RunWithContext(...)` и `RunWebHookWithContext(...)` не заменяют `Close()`: локальные ресурсы всё равно нужно закрывать отдельно. +`RunWithContext(...)` и `RunWebhookWithContext(...)` не заменяют `Close()`: локальные ресурсы всё равно нужно закрывать отдельно. ## Частые ошибки diff --git a/Bot-Lifecycle.md b/Bot-Lifecycle.md index 2f0da22..2533e0b 100644 --- a/Bot-Lifecycle.md +++ b/Bot-Lifecycle.md @@ -7,7 +7,7 @@ This page explains how a `Bot` is created, configured, started, stopped, and ret ## Lifecycle at a glance 1. Build `BotOpts` and call `NewBot`. 2. Configure the bot instance: prefixes, plugins, middleware, runners, localization, payload defaults, and optional app data. -3. Start it with `RunWithContext(ctx)`, `Run()`, or `RunWebHookWithContext(...)`. +3. Start it with `RunWithContext(ctx)`, `Run()`, or `RunWebhookWithContext(...)`. 4. Stop runtime by canceling the context or letting the run method return. 5. Call `Close()` to release local resources. 6. Create a new `Bot` if you need another run. @@ -66,7 +66,7 @@ Laniakea has three practical configuration phases: 1. Construction and bot setup after `NewBot[T](opts)`. 2. Plugin snapshotting at `AddPlugins(...)`. -3. Runtime freeze after the first `Run()`, `RunWithContext(...)`, or `RunWebHookWithContext(...)`. +3. Runtime freeze after the first `Run()`, `RunWithContext(...)`, or `RunWebhookWithContext(...)`. That means: - finish plugin structure before `AddPlugins(...)`; @@ -97,10 +97,10 @@ if err != nil { defer bot.Close() plugin := laniakea.NewPlugin[laniakea.NoData]("main") -plugin.NewCommand(func(ctx *laniakea.MsgContext, db laniakea.NoData) error { +plugin.Command("ping", func(ctx *laniakea.MsgContext, db laniakea.NoData) error { ctx.Answer("pong") return nil -}, "ping") +}) bot.AddPlugins(plugin) @@ -109,7 +109,7 @@ if err := bot.Run(); err != nil { } ``` -## `RunWithContext(...)`, `Run()`, and `RunWebHookWithContext(...)` +## `RunWithContext(...)`, `Run()`, and `RunWebhookWithContext(...)` `RunWithContext(ctx)` is the main polling runtime entry point. @@ -124,16 +124,16 @@ It: `Run()` is only a shorthand for `RunWithContext(context.Background())`. -`RunWebHookWithContext(...)` is the webhook runtime entry point. It shares the same: +`RunWebhookWithContext(...)` is the webhook runtime entry point. It shares the same: - single-use rule; - runner startup behavior; - internal update queue; - worker-pool dispatch model; - graceful shutdown semantics. -Use `RunWithContext(...)` for production services that poll Telegram directly. Use `RunWebHookWithContext(...)` when Telegram should deliver updates through your HTTP endpoint. +Use `RunWithContext(...)` for production services that poll Telegram directly. Use `RunWebhookWithContext(...)` when Telegram should deliver updates through your HTTP endpoint. -If you switch an existing deployment from webhook delivery to polling, remove the current webhook first with `CloseWebHook()` or low-level `tgapi.DeleteWebhook(...)`. Telegram keeps webhook delivery active until the webhook is deleted. +If you switch an existing deployment from webhook delivery to polling, remove the current webhook first with `CloseWebhook()` or low-level `tgapi.DeleteWebhook(...)`. Telegram keeps webhook delivery active until the webhook is deleted. For the webhook-specific option model, transport behavior, and operational guidance, see [[Webhook-Runtime]]. @@ -172,7 +172,7 @@ Canceling the runtime context tells the bot to stop accepting new work and finis - one-time async runners to finish; - background runners to exit after noticing `ctx.Done()`. -`RunWithContext(...)` and `RunWebHookWithContext(...)` do not automatically release API, uploader, or logger resources. You still need to call `Close()`. +`RunWithContext(...)` and `RunWebhookWithContext(...)` do not automatically release API, uploader, or logger resources. You still need to call `Close()`. ## `Close()` versus `CloseRemote()` @@ -192,7 +192,7 @@ Use `CloseRemote(ctx)` only when you specifically need Telegram-side session shu ## Single-use rule -A `Bot` cannot be started twice. After `Run()`, `RunWithContext(...)`, or `RunWebHookWithContext(...)` returns, later start attempts fail with `ErrBotAlreadyRun`. +A `Bot` cannot be started twice. After `Run()`, `RunWithContext(...)`, or `RunWebhookWithContext(...)` returns, later start attempts fail with `ErrBotAlreadyRun`. That means: - do not call a runtime entry point again after a graceful stop; diff --git a/Bot-Options-and-Configuration-RU.md b/Bot-Options-and-Configuration-RU.md index a9a0ae6..7de2ece 100644 --- a/Bot-Options-and-Configuration-RU.md +++ b/Bot-Options-and-Configuration-RU.md @@ -46,10 +46,10 @@ opts := laniakea.LoadOptsFromEnv() `LoadBotOptsFile(...)` подходит, когда конфиг бота удобнее хранить в отдельном файле. Из коробки доступно: -- `BotOptsFileJsonCodec` для JSON. +- `BotOptsFileJSONCodec` для JSON. ```go -codec := laniakea.BotOptsFileJsonCodec{} +codec := laniakea.BotOptsFileJSONCodec{} opts, err := laniakea.LoadBotOptsFile(codec, "config.json") if err != nil { return err @@ -67,7 +67,7 @@ if err := laniakea.SaveBotOptsFile(codec, "config.json", opts); err != nil { Перед декодированием loader разворачивает плейсхолдеры вроде `{{ TG_TOKEN }}` из environment variables. Из коробки библиотека пока поддерживает только JSON. Для других форматов можно реализовать свой codec через `BotOptsFileCodec`. -Если нужен другой формат, например TOML, используй `BotOptsFileJsonCodec` как референсную реализацию собственного codec. +Если нужен другой формат, например TOML, используй `BotOptsFileJSONCodec` как референсную реализацию собственного codec. ## Что обязательно @@ -128,7 +128,7 @@ if err := laniakea.SaveBotOptsFile(codec, "config.json", opts); err != nil { Управляют записью логов в файлы. -### `UseTestServer` и `APIUrl` +### `UseTestServer` и `APIURL` Полезны для тестового окружения, proxy или собственного Telegram gateway. diff --git a/Bot-Options-and-Configuration.md b/Bot-Options-and-Configuration.md index 5a225c0..8166954 100644 --- a/Bot-Options-and-Configuration.md +++ b/Bot-Options-and-Configuration.md @@ -58,10 +58,10 @@ This is often the easiest approach for containers, CI, and production services. Use `LoadBotOptsFile(...)` when you want to keep bot configuration in a checked-in or deployment-managed config file. Built in: -- `BotOptsFileJsonCodec` for JSON files. +- `BotOptsFileJSONCodec` for JSON files. ```go -codec := laniakea.BotOptsFileJsonCodec{} +codec := laniakea.BotOptsFileJSONCodec{} opts, err := laniakea.LoadBotOptsFile(codec, "config.json") if err != nil { return err @@ -84,7 +84,7 @@ if err := laniakea.SaveBotOptsFile(codec, "config.json", opts); err != nil { Before decoding, the loader expands placeholders like `{{ TG_TOKEN }}` from environment variables. Only JSON support is built into the library right now. If you want another format, implement `BotOptsFileCodec` yourself. -Use `BotOptsFileJsonCodec` as the reference implementation for custom codecs such as TOML. +Use `BotOptsFileJSONCodec` as the reference implementation for custom codecs such as TOML. ## Required setting @@ -222,12 +222,12 @@ Env: Use this only for development and testing scenarios that explicitly target Telegram's test environment. -### `APIUrl` +### `APIURL` Overrides the default Telegram API base URL. Setter: -- `SetAPIUrl(url)` +- `SetAPIURL(url)` Env: - `API_URL` diff --git a/Commands-and-Plugins-RU.md b/Commands-and-Plugins-RU.md index dde43d9..b61a6ca 100644 --- a/Commands-and-Plugins-RU.md +++ b/Commands-and-Plugins-RU.md @@ -73,7 +73,7 @@ func start(ctx *laniakea.MsgContext, db *App) error { ```go plugin := laniakea.NewPlugin[*App]("main") -plugin.AddCommand(plugin.NewCommand(start, "start")) +plugin.Command("start", start) ``` Важно: @@ -102,12 +102,12 @@ plugin.AddCommand(plugin.NewCommand(start, "start")) Пример: ```go -plugin.AddCommand( - plugin.NewCommand(banUser, "ban", - laniakea.NewCommandArg("user_id"). - SetValueType(laniakea.CommandValueIntType). - SetRequired(), - ), +plugin.Command( + "ban", + banUser, + laniakea.NewCommandArg("user_id"). + SetValueType(laniakea.CommandValueInt). + SetRequired(), ) ``` @@ -130,7 +130,7 @@ func confirmDelete(ctx *laniakea.MsgContext, db *App) error { return nil } -plugin.AddPayload(plugin.NewPayload(confirmDelete, "delete.confirm")) +plugin.Payload("delete.confirm", confirmDelete) ``` Важно помнить: @@ -204,7 +204,7 @@ plugin.AddMiddleware(...) Добавляется через: ```go -plugin.NewCommand(handler, "name").Use(middleware) +plugin.Command("name", handler).Use(middleware) ``` Подходит, когда проверка нужна только одной команде или одному обработчику данных callback. @@ -218,13 +218,13 @@ plugin.NewCommand(handler, "name").Use(middleware) Неправильно: ```go -plugin.NewCommand(start, "/start") +plugin.Command("/start", start) ``` Правильно: ```go -plugin.NewCommand(start, "start") +plugin.Command("start", start) ``` ### Считать данные callback обычной командой diff --git a/Commands-and-Plugins.md b/Commands-and-Plugins.md index 0b55bbd..98ade87 100644 --- a/Commands-and-Plugins.md +++ b/Commands-and-Plugins.md @@ -71,11 +71,11 @@ func start(ctx *laniakea.MsgContext, db *App) error { ## Registering commands -Create a command with `NewCommand(...)` and add it to a plugin: +Create and register a command with `Plugin.Command(...)`: ```go plugin := laniakea.NewPlugin[*App]("main") -plugin.AddCommand(plugin.NewCommand(start, "start")) +plugin.Command("start", start) ``` The command name: @@ -99,7 +99,7 @@ func echo(ctx *laniakea.MsgContext, db laniakea.NoData) error { return nil } -plugin.AddCommand(plugin.NewCommand(echo, "echo")) +plugin.Command("echo", echo) ``` For `/echo hello world`: @@ -113,12 +113,12 @@ You can declare command arguments using `CommandArg`. Example: ```go -plugin.AddCommand( - plugin.NewCommand(banUser, "ban", - laniakea.NewCommandArg("user_id"). - SetValueType(laniakea.CommandValueIntType). - SetRequired(), - ), +plugin.Command( + "ban", + banUser, + laniakea.NewCommandArg("user_id"). + SetValueType(laniakea.CommandValueInt). + SetRequired(), ) ``` @@ -133,7 +133,7 @@ If validation fails, the command does not run and the bot error path is used. Payload handlers are for callback data coming from inline keyboard buttons. -Register them with `NewPayload(...)` or `AddPayload(...)`: +Register them with `Plugin.Payload(...)` or `AddPayload(...)`: ```go func confirmDelete(ctx *laniakea.MsgContext, db *App) error { @@ -141,7 +141,7 @@ func confirmDelete(ctx *laniakea.MsgContext, db *App) error { return nil } -plugin.AddPayload(plugin.NewPayload(confirmDelete, "delete.confirm")) +plugin.Payload("delete.confirm", confirmDelete) ``` Payload handlers: @@ -213,7 +213,7 @@ Use this for logic shared by most handlers in the plugin. Added with: ```go -plugin.NewCommand(handler, "name").Use(middleware) +plugin.Command("name", handler).Use(middleware) ``` Use this when only one command or payload needs the check. @@ -243,20 +243,20 @@ admin.AddMiddleware(laniakea.NewMiddleware("admin-only", func(ctx *laniakea.MsgC return true })) -admin.AddCommand(admin.NewCommand(func(ctx *laniakea.MsgContext, app *App) error { +admin.Command("ban", func(ctx *laniakea.MsgContext, app *App) error { ctx.Answer("Banned") return nil -}, "ban")) +}) ``` ### Example: payload handler for inline keyboard callback ```go -plugin.AddPayload(plugin.NewPayload(func(ctx *laniakea.MsgContext, app *App) error { - ctx.AnswerCbQueryText("Accepted") +plugin.Payload("approve", func(ctx *laniakea.MsgContext, app *App) error { + ctx.AnswerCallbackText("Accepted") ctx.EditCallback("Done", nil) return nil -}, "approve")) +}) ``` ## Common mistakes @@ -266,13 +266,13 @@ plugin.AddPayload(plugin.NewPayload(func(ctx *laniakea.MsgContext, app *App) err Wrong: ```go -plugin.NewCommand(start, "/start") +plugin.Command("/start", start) ``` Right: ```go -plugin.NewCommand(start, "start") +plugin.Command("start", start) ``` ### Treating payloads like commands diff --git a/Drafts.md b/Drafts.md index 1a8c8f1..296449e 100644 --- a/Drafts.md +++ b/Drafts.md @@ -118,8 +118,8 @@ That last detail matters: if a `Push(...)` call makes the draft too large, you s Each draft gets a provider-generated `ID`. ID generation modes: -- random IDs from `RandomDraftIdGenerator`; -- monotonic IDs from `LinearDraftIdGenerator`. +- random IDs from `RandomDraftIDGenerator`; +- monotonic IDs from `LinearDraftIDGenerator`. The ID is mainly useful when: - correlating draft activity in logs; diff --git a/Error-Handling-RU.md b/Error-Handling-RU.md index 3cd587d..8a6f7a9 100644 --- a/Error-Handling-RU.md +++ b/Error-Handling-RU.md @@ -88,7 +88,7 @@ if err := db.WarmCache(); err != nil { Для callback-потока возвращённая ошибка превращается не в обычное сообщение в чат, а в ответ на callback query. Если нужен другой UX, лучше: -- вызвать `AnswerCbQueryText(...)` или `AnswerCbQueryAlert(...)`; +- вызвать `AnswerCallbackText(...)` или `AnswerCallbackAlert(...)`; - вернуть `nil`. ## `ErrorTemplate(...)` diff --git a/Error-Handling.md b/Error-Handling.md index 7daa5ad..0db5033 100644 --- a/Error-Handling.md +++ b/Error-Handling.md @@ -133,10 +133,10 @@ This matters because callback error UX is different: - no new chat message is posted for the error path. If you want a different callback UX, answer manually with: -- `AnswerCbQuery()` -- `AnswerCbQueryText(...)` -- `AnswerCbQueryAlert(...)` -- `AnswerCbQueryUrl(...)` +- `AnswerCallback()` +- `AnswerCallbackText(...)` +- `AnswerCallbackAlert(...)` +- `AnswerCallbackURL(...)` and return `nil`. @@ -183,38 +183,38 @@ Related page: ### Centralized command failure ```go -plugin.NewCommand(func(ctx *laniakea.MsgContext, db *App) error { +plugin.Command("report", func(ctx *laniakea.MsgContext, db *App) error { result, err := db.DoWork() if err != nil { return fmt.Errorf("failed to build report: %w", err) } ctx.Answer(result) return nil -}, "report") +}) ``` ### Manual denial response ```go -plugin.NewCommand(func(ctx *laniakea.MsgContext, db *App) error { +plugin.Command("admin", func(ctx *laniakea.MsgContext, db *App) error { if ctx.From == nil || !db.Allowed(ctx.From.ID) { ctx.Answer("Access denied") return nil } return doProtectedWork(ctx, db) -}, "admin") +}) ``` ### Callback-specific manual alert ```go -plugin.NewPayload(func(ctx *laniakea.MsgContext, db *App) error { +plugin.Payload("start", func(ctx *laniakea.MsgContext, db *App) error { if !ready { - ctx.AnswerCbQueryAlert("This action is not available yet") + ctx.AnswerCallbackAlert("This action is not available yet") return nil } return nil -}, "start") +}) ``` ## Recommendations diff --git a/Framework-Backlog-RU.md b/Framework-Backlog-RU.md index b83156b..19b11a3 100644 --- a/Framework-Backlog-RU.md +++ b/Framework-Backlog-RU.md @@ -9,7 +9,7 @@ Текущее состояние: - В репозитории уже есть низкоуровневые API для настройки webhook на уровне `tgapi`: `SetWebhook(...)`, `DeleteWebhook(...)`, `GetWebhookInfo(...)`, а также поддержка загрузки сертификата через uploader. -- Во фреймворке теперь есть полноценные bot-level точки входа webhook runtime: `RunWebHookWithContext(...)` и `RunWebHook(...)`. +- Во фреймворке теперь есть полноценные bot-level точки входа webhook runtime: `RunWebhookWithContext(...)` и `RunWebhook(...)`. - Webhook-доставка теперь использует ту же внутреннюю очередь update-ов, тот же worker pool, тот же запуск runners и тот же single-use runtime contract, что и polling. - Поведение webhook runtime, security-модель и правила перехода обратно на polling теперь описаны в основной документации и wiki. @@ -21,12 +21,12 @@ Что теперь есть: -- `BotWebHookOpts`, `NewBotWebHookOpts()` и fluent helper-методы для webhook-конфигурации. -- `RunWebHookWithContext(...)` и `RunWebHook(...)` как bot-owned точки входа runtime. +- `BotWebhookOpts`, `NewBotWebhookOpts()` и fluent helper-методы для webhook-конфигурации. +- `RunWebhookWithContext(...)` и `RunWebhook(...)` как bot-owned точки входа runtime. - Общая queued dispatch-модель, worker-pool обработка, запуск runners и single-use semantics для polling и webhook mode. - Fallback webhook `AllowedUpdates` к bot-level конфигурации типов update. - Валидация webhook path и количества TLS-файлов до remote webhook setup. -- Явное удаление remote webhook через `CloseWebHook()` или низкоуровневый `tgapi.DeleteWebhook(...)` при переходе deployment с webhook-доставки обратно на polling. +- Явное удаление remote webhook через `CloseWebhook()` или низкоуровневый `tgapi.DeleteWebhook(...)` при переходе deployment с webhook-доставки обратно на polling. - Регрессионные тесты на queue delivery, запуск runners, single-use behavior, rejection слишком большого body, path/TLS validation и auth-поведение status endpoint. Практическая цель: @@ -117,7 +117,7 @@ Текущее состояние: -- Фреймворк теперь считает конфигурацию бота структурно завершённой после начала первого runtime entry point: `Run()`, `RunWithContext(...)` или `RunWebHookWithContext(...)`. +- Фреймворк теперь считает конфигурацию бота структурно завершённой после начала первого runtime entry point: `Run()`, `RunWithContext(...)` или `RunWebhookWithContext(...)`. - Поздние bot-level попытки мутации больше не применяются частично после старта runtime. - Границы между регистрацией плагинов, стартом runtime и фиксацией конфигурации теперь оформлены как явное поведение фреймворка и закреплены тестами. @@ -189,7 +189,7 @@ Текущее направление API: - `Scene`, `SceneContext`, `SceneSession` и `SessionStore`. -- `Plugin.NewScene(...)` и `Plugin.AddScene(...)`. +- `Plugin.Scene(...)` и `Plugin.AddScene(...)`. - `MsgContext.EnterScene(...)`, `EnterSceneStep(...)` и `ExitScene(...)`. - `SceneContext.Stay()`, `Next(...)`, `Exit()`, `Pass()`, `BindData(...)` и `SaveData(...)`. - Состояние на пользователя или чат с хранением в `SessionStore` и чистым интерфейсом для собственного постоянного хранения. @@ -263,7 +263,7 @@ func ban(ctx *laniakea.MsgContext, db *App) error { Текущее состояние: -- `RunWithContext(...)` и `RunWebHookWithContext(...)` управляют жизненным циклом выполнения бота и корректным завершением. +- `RunWithContext(...)` и `RunWebhookWithContext(...)` управляют жизненным циклом выполнения бота и корректным завершением. - `tgapi` уже поддерживает методы, принимающие `context.Context`. - Обычные обработчики не получают полноценный `context.Context`, привязанный к обработке конкретного запроса. diff --git a/Framework-Backlog.md b/Framework-Backlog.md index 7f6bda3..5168db7 100644 --- a/Framework-Backlog.md +++ b/Framework-Backlog.md @@ -4,12 +4,57 @@ This page tracks framework-level backlog items that are about missing concepts i ## Done +### [1.0.0] + +### Major — close before 1.0.0 tag + +- [X] **M1. `BotPayloadType*` are `var`, must be `const`** — `bot.go:50-59`. Public sentinels are user-mutable globals. `KeyboardButtonStyle*` in `keyboard.go:10-17` already uses `const`; match the pattern. +- [X] **M2. `Observer` method naming asymmetry** — `observer.go:147-157`. `OnReceiveUpdate` → `OnUpdateReceived`; `OnHandledUpdate` → `OnUpdateHandled` to match `UpdateReceivedEvent` / `UpdateHandledEvent` and the rest of the `OnX` pattern. Breaking after 1.0. +- [X] **M3. Uploader returns ad-hoc error string instead of `*ResponseError`** — `tgapi/uploader_api.go:183`. `tgapi/api.go:258-292` returns `*ResponseError`; uploader must do the same so `errors.As(err, &tgapi.ResponseError{})` works for upload paths too. +- [X] **M4. `BotOptsFileJSON` is missing `PollTimeout`** — `bot_opts_loader.go:35-46`, plus `FromBytes`/`ToBytes` mapping. File round-trip silently drops `PollTimeout`. +- [X] **M5. Stale `Bot.Updates` godoc** — `methods.go:11-44`. Claims "30-second timeout" and "empty slice if none"; in reality timeout is `bot.pollTimeout` and the function returns `nil` on error. +- [X] **M6. Self-contradicting `NewRandomDraftProvider` godoc** — `drafts.go:50-59`. Says "cryptographically secure random numbers" but uses `math/rand/v2` (the underlying generator type correctly notes it is not crypto-secure). +- [X] **M7. `Draft.Delete` godoc says "internal method"** — `drafts.go:190-201`. Method is exported; either rewrite the godoc with a public-intent description or unexport. +- [X] **M8. Russian comments in production code** + - `msg_handler.go:28` — "Ищем команду по точному совпадению" + - `tgapi/uploader_api.go:181` — "Повторяем запрос" +- [X] **M9. `MessageContext.Error` godoc references unexported helper** — `msg_context.go:540`. "Error is an alias for error()" — rewrite to describe the centralized handler error path and `IsUserError` gating. +- [X] **M10. `Scene` and `SceneSession` mix exported fields with setters** + - `Scene` exports `Name/Scope/Entry/PluginName` and also has `SetScope/SetEntry`; `PluginName` is framework-assigned but publicly mutable. + - `SceneSession` exports `Data []byte` and also has `Set/Get/HasData/ClearData/BindData/SaveData`. + - Pick one model per type before 1.0.0. +- [X] **M11. Constant-time compare for webhook secret** — `bot_webhook.go:296` (update handler) and `bot_webhook.go:341` (`/status`). Use `subtle.ConstantTimeCompare`. + +### Minor — can slip to 1.0.x + +- [X] Strip `// Internal helper …` godoc from unexported funcs (~23 occurrences in repo); `AGENTS.md` explicitly forbids godoc-style comments on unexported declarations without a strong reason. +- [X] `Plugin.AddCommand` godoc references unexported field `.command` — `plugins.go:48-49`. +- [X] `Runner` builder naming: `runner.Once(true)`, `runner.Async(true)` read awkwardly; consider `SetOnce`/`SetAsync` to match `Set*` on other types, or zero-arg `Once()` + paired `Repeat(every)`. +- [X] Typo in webhook error string: `bot_webhook.go:143` — "MaxConnections must between 1 and 100" (missing `be`). +- [X] `RunWebhookWithContext` uses inline `errors.New(...)` instead of `Err*` sentinels (`bot_webhook.go:131-156`); rest of the package uses sentinels from `errors.go`. +- [X] `tgapi.UpdateTypeManagedBot` (`tgapi/types.go:61`) has no godoc. +- [X] `Bot.GetAPI`, `Bot.GetUploader`, `InlineKeyboard.GetMaxRow` have no godoc. +- [X] `Bot.L10n` godoc says "Returns empty string if translation not found"; actually returns the key (`l10n.go:48-59`). +- [X] `Bot.handle` panic recovery only logs — emit `ErrorEvent` so observers see panics (`handler.go:18-23`). +- [X] `handleCallback` vs `handleMessage` differ in plugin-logger assignment: callback assigns unconditionally then falls back to bot logger (`msg_handler.go:209-212`); message only assigns if non-nil (`msg_handler.go:35-37`). Align. +- [X] `SetCallbackData` godoc says "default payload type is JSON" — actually the zero `BotPayloadType` falls through to the `default` branch (which happens to be JSON). Either document the zero-value behavior explicitly or initialize the builder with the bot's default (`keyboard.go:106-122`). +- [X] `commands.go:62-66` — empty `case CommandValueAny:` next to `default: regex = nil` looks like an incomplete switch. Merge or add a one-line comment. +- [X] `Bot.SetDebug` does not call `configMutable` unlike sibling setters; if intentional, note it in godoc. + +### Tests to add after the fixes + +- [X] `BotOptsFileJSON` round-trip for `PollTimeout` (after M4). +- [X] Uploader 4xx/429 surfaces `*tgapi.ResponseError` (after M3). +- [X] `Bot.handle` panic → observer receives `ErrorEvent` (after panic-recovery fix). +- [X] Webhook `/status` with wrong `SecretToken` returns 403 / `403`-equivalent (after M11), incl. a constant-time-compare smoke. +- [X] Table-driven `parseCommand` cases for `/cmd@botname` and stripping behavior. + ### [1.0.0-rc.14] Webhook runtime model Current state: - The repository already exposes low-level Telegram webhook setup APIs through `tgapi`, including `SetWebhook(...)`, `DeleteWebhook(...)`, `GetWebhookInfo(...)`, and uploader-based certificate upload support. -- The framework now exposes first-class bot-level webhook runtime entry points through `RunWebHookWithContext(...)` and `RunWebHook(...)`. +- The framework now exposes first-class bot-level webhook runtime entry points through `RunWebhookWithContext(...)` and `RunWebhook(...)`. - Webhook delivery now uses the same internal update queue, worker pool, runner startup model, and single-use runtime contract as polling. - The webhook runtime behavior, security model, and polling-transition requirements are now documented in the main docs and wiki. @@ -21,12 +66,12 @@ Why this matters: What is now present: -- `BotWebHookOpts`, `NewBotWebHookOpts()`, and fluent helpers for webhook-specific configuration. -- `RunWebHookWithContext(...)` and `RunWebHook(...)` as bot-owned runtime entry points. +- `BotWebhookOpts`, `NewBotWebhookOpts()`, and fluent helpers for webhook-specific configuration. +- `RunWebhookWithContext(...)` and `RunWebhook(...)` as bot-owned runtime entry points. - Shared queued update dispatch, worker-pool delivery, runner startup, and single-use run semantics between polling and webhook modes. - Default fallback from webhook `AllowedUpdates` to the bot-level update type configuration. - Request validation for webhook path shape and TLS file count before remote webhook setup. -- Explicit remote webhook teardown through `CloseWebHook()` or low-level `tgapi.DeleteWebhook(...)` when switching a deployment from webhook delivery back to polling. +- Explicit remote webhook teardown through `CloseWebhook()` or low-level `tgapi.DeleteWebhook(...)` when switching a deployment from webhook delivery back to polling. - Regression coverage for queue delivery, runner startup, single-use behavior, body-size rejection, path and TLS validation, and status-endpoint auth behavior. Practical target: @@ -117,7 +162,7 @@ Practical target: Current state: -- The framework now treats bot configuration as structurally complete once the first runtime entry point begins: `Run()`, `RunWithContext(...)`, or `RunWebHookWithContext(...)`. +- The framework now treats bot configuration as structurally complete once the first runtime entry point begins: `Run()`, `RunWithContext(...)`, or `RunWebhookWithContext(...)`. - Late bot-level mutation attempts no longer partially apply after runtime startup. - Plugin registration and runtime configuration boundaries are now documented and tested as explicit framework behavior. @@ -189,7 +234,7 @@ What is still missing or not yet settled: Current API direction: - `Scene`, `SceneContext`, `SceneSession`, and `SessionStore`. -- `Plugin.NewScene(...)` and `Plugin.AddScene(...)`. +- `Plugin.Scene(...)` and `Plugin.AddScene(...)`. - `MsgContext.EnterScene(...)`, `EnterSceneStep(...)`, and `ExitScene(...)`. - `SceneContext.Stay()`, `Next(...)`, `Exit()`, `Pass()`, `BindData(...)`, and `SaveData(...)`. - Storage-backed per-user or per-chat state with a clean interface for custom persistence. @@ -263,7 +308,7 @@ Practical target: Current state: -- `RunWithContext(...)` and `RunWebHookWithContext(...)` control bot runtime lifecycle and graceful shutdown. +- `RunWithContext(...)` and `RunWebhookWithContext(...)` control bot runtime lifecycle and graceful shutdown. - `tgapi` already supports context-aware methods. - Regular handlers do not receive a first-class request-scoped `context.Context`. diff --git a/Getting-Started-RU.md b/Getting-Started-RU.md index 306c0d3..24fcf52 100644 --- a/Getting-Started-RU.md +++ b/Getting-Started-RU.md @@ -48,7 +48,7 @@ func main() { defer bot.Close() plugin := laniakea.NewPlugin[laniakea.NoData]("main") - plugin.AddCommand(plugin.NewCommand(ping, "ping")) + plugin.Command("ping", ping) bot.AddPlugins(plugin) @@ -100,7 +100,7 @@ bot.SetAppData(app) ```go plugin := laniakea.NewPlugin[laniakea.NoData]("admin") -plugin.AddCommand(plugin.NewCommand(ping, "ping")) +plugin.Command("ping", ping) bot.AddPlugins(plugin) ``` @@ -136,7 +136,7 @@ func profile(ctx *laniakea.MsgContext, db *App) error { ### 4. `Bot` single-use -После `Run()`, `RunWithContext(...)` или `RunWebHookWithContext(...)` нельзя снова запускать тот же экземпляр `Bot`. +После `Run()`, `RunWithContext(...)` или `RunWebhookWithContext(...)` нельзя снова запускать тот же экземпляр `Bot`. Правильная модель: - создать bot @@ -151,7 +151,7 @@ func profile(ctx *laniakea.MsgContext, db *App) error { ### 5. `Close()` все равно нужен -Даже если ты используешь `Run()`, `RunWithContext(...)` или `RunWebHookWithContext(...)`, ресурсы нужно закрывать явно: +Даже если ты используешь `Run()`, `RunWithContext(...)` или `RunWebhookWithContext(...)`, ресурсы нужно закрывать явно: ```go defer bot.Close() @@ -168,7 +168,7 @@ defer bot.Close() 5. Добавить команды, payloads и middleware в плагины 6. Зарегистрировать плагины через `AddPlugins(...)` 7. При необходимости вызвать `AutoGenerateCommands()` -8. Вызвать `Run()`, `RunWithContext(...)` или `RunWebHookWithContext(...)` +8. Вызвать `Run()`, `RunWithContext(...)` или `RunWebhookWithContext(...)` 9. Закрыть bot через `Close()` ## Частые ошибки на старте diff --git a/Getting-Started.md b/Getting-Started.md index 4602768..b66b954 100644 --- a/Getting-Started.md +++ b/Getting-Started.md @@ -49,7 +49,7 @@ func main() { defer bot.Close() plugin := laniakea.NewPlugin[laniakea.NoData]("main") - plugin.AddCommand(plugin.NewCommand(ping, "ping")) + plugin.Command("ping", ping) bot.AddPlugins(plugin) @@ -102,7 +102,7 @@ Example: ```go plugin := laniakea.NewPlugin[laniakea.NoData]("admin") -plugin.AddCommand(plugin.NewCommand(ping, "ping")) +plugin.Command("ping", ping) bot.AddPlugins(plugin) ``` @@ -139,7 +139,7 @@ func profile(ctx *laniakea.MsgContext, db *App) error { A `Bot` instance is single-use. -After `Run()`, `RunWithContext(...)`, or `RunWebHookWithContext(...)` returns: +After `Run()`, `RunWithContext(...)`, or `RunWebhookWithContext(...)` returns: - do not call a runtime entry point again on the same bot - create a new bot instance for the next run @@ -149,7 +149,7 @@ See [[Bot-Lifecycle]] for details. ### 5. Always close the bot -`Run()`, `RunWithContext(...)`, and `RunWebHookWithContext(...)` do not replace `Close()`. +`Run()`, `RunWithContext(...)`, and `RunWebhookWithContext(...)` do not replace `Close()`. You should still release bot-owned resources explicitly: @@ -168,7 +168,7 @@ For most bots, this order is the least surprising: 5. Add commands, payloads, and middleware to plugins 6. Register plugins with `AddPlugins(...)` 7. Optionally call `AutoGenerateCommands()` -8. Call `Run()`, `RunWithContext(...)`, or `RunWebHookWithContext(...)` +8. Call `Run()`, `RunWithContext(...)`, or `RunWebhookWithContext(...)` 9. Call `Close()` when done ## A slightly more realistic example @@ -207,7 +207,7 @@ func main() { bot.SetAppData(&App{}) plugin := laniakea.NewPlugin[*App]("main") - plugin.AddCommand(plugin.NewCommand(echo, "echo")) + plugin.Command("echo", echo) bot.AddPlugins(plugin) diff --git a/Inline-Keyboards-and-Payloads-RU.md b/Inline-Keyboards-and-Payloads-RU.md index 09460c1..db4623f 100644 --- a/Inline-Keyboards-and-Payloads-RU.md +++ b/Inline-Keyboards-and-Payloads-RU.md @@ -15,17 +15,17 @@ English version: [[Inline-Keyboards-and-Payloads]] ## Как строить клавиатуру Основные конструкторы: -- `NewInlineKeyboardJson(maxRow)` +- `NewInlineKeyboardJSON(maxRow)` - `NewInlineKeyboardBase64(maxRow)` - `NewInlineKeyboard(payloadType, maxRow)` Пример: ```go -kb := laniakea.NewInlineKeyboardJson(2). +kb := laniakea.NewInlineKeyboardJSON(2). AddCallbackButton("Open", "open", 42). AddCallbackButton("Delete", "delete", 42). - AddUrlButton("Docs", "https://example.com/docs") + AddURLButton("Docs", "https://example.com/docs") ``` `maxRow` определяет, сколько кнопок автоматически помещается в один ряд. @@ -54,7 +54,7 @@ kb := laniakea.NewInlineKeyboardJson(2). ## JSON vs Base64 -`BotPayloadJson`: +`BotPayloadJSON`: - удобнее читать в логах и тестах; - проще отлаживать. @@ -85,7 +85,7 @@ bot.SetStrictPayloadType(true) ```go kb := ctx.NewInlineKeyboard(2). - SetPayloadType(laniakea.BotPayloadJson) + SetPayloadType(laniakea.BotPayloadJSON) ``` Это удобно для сценариев миграции или отладки. diff --git a/Inline-Keyboards-and-Payloads.md b/Inline-Keyboards-and-Payloads.md index af65ac6..2a7e357 100644 --- a/Inline-Keyboards-and-Payloads.md +++ b/Inline-Keyboards-and-Payloads.md @@ -16,7 +16,7 @@ Inline keyboards in Laniakea are built explicitly: you choose a row width, add U ## Building a keyboard The most direct constructors are: -- `laniakea.NewInlineKeyboardJson(maxRow)` +- `laniakea.NewInlineKeyboardJSON(maxRow)` - `laniakea.NewInlineKeyboardBase64(maxRow)` - `laniakea.NewInlineKeyboard(payloadType, maxRow)` @@ -25,10 +25,10 @@ The most direct constructors are: Example: ```go -kb := laniakea.NewInlineKeyboardJson(2). +kb := laniakea.NewInlineKeyboardJSON(2). AddCallbackButton("Open", "open", 42). AddCallbackButton("Delete", "delete", 42). - AddUrlButton("Docs", "https://example.com/docs") + AddURLButton("Docs", "https://example.com/docs") ``` In that example: @@ -77,7 +77,7 @@ All payload arguments are converted with `fmt.Sprint`, so handlers receive strin Example: ```go -kb := laniakea.NewInlineKeyboardJson(1). +kb := laniakea.NewInlineKeyboardJSON(1). AddCallbackButton("Ban", "ban_user", 12345, "spam") ``` @@ -97,7 +97,7 @@ There is no separate `ctx.Payload` object in the current API. Payload handlers r Laniakea supports two payload encodings: -- `BotPayloadJson` +- `BotPayloadJSON` - `BotPayloadBase64` JSON is easier to inspect in logs and tests. @@ -132,7 +132,7 @@ For a single keyboard, you can override it locally: ```go kb := ctx.NewInlineKeyboard(2). - SetPayloadType(laniakea.BotPayloadJson). + SetPayloadType(laniakea.BotPayloadJSON). AddCallbackButton("Inspect", "inspect", 7) ``` @@ -194,23 +194,23 @@ In both cases, keep payloads short and intentional. Telegram callback data is li ## Button builder for advanced cases -`InlineKbButtonBuilder` is the flexible path when you want button-specific styling or custom emoji icons. +`InlineKeyboardButtonBuilder` is the flexible path when you want button-specific styling or custom emoji icons. Example: ```go -button := laniakea.NewInlineKbButton("Confirm"). +button := laniakea.NewInlineKeyboardButton("Confirm"). SetStyle(laniakea.ButtonStyleSuccess). - SetCallbackDataJson("confirm_order", 99) + SetCallbackDataJSON("confirm_order", 99) -kb := laniakea.NewInlineKeyboardJson(2).AddButton(button) +kb := laniakea.NewInlineKeyboardJSON(2).AddButton(button) ``` The builder supports: - `SetStyle(...)` -- `SetUrl(...)` -- `SetIconCustomEmojiId(...)` -- `SetCallbackDataJson(...)` +- `SetURL(...)` +- `SetIconCustomEmojiID(...)` +- `SetCallbackDataJSON(...)` - `SetCallbackDataBase64(...)` Use it when `AddCallbackButton(...)` is not expressive enough. @@ -223,9 +223,9 @@ Laniakea exposes three convenience style constants: - `ButtonStyleDanger` You can use them with: -- `AddUrlButtonStyle(...)` +- `AddURLButtonStyle(...)` - `AddCallbackButtonStyle(...)` -- `InlineKbButtonBuilder.SetStyle(...)` +- `InlineKeyboardButtonBuilder.SetStyle(...)` URL buttons and callback buttons can live in the same keyboard. Use URL buttons for external navigation and callback buttons for bot-side actions. @@ -241,17 +241,17 @@ If your plain-text reply may exceed Telegram’s message length limit, use: ## Routing payloads to handlers Payloads are registered on plugins with: -- `Plugin.NewPayload(...)` +- `Plugin.Payload(...)` - `Plugin.AddPayload(...)` Example: ```go -plugin.NewPayload(func(ctx *laniakea.MsgContext, db *App) error { +plugin.Payload("inspect", func(ctx *laniakea.MsgContext, db *App) error { id := ctx.Args[0] - ctx.AnswerCbQueryText("Handled " + id) + ctx.AnswerCallbackText("Handled " + id) return nil -}, "inspect") +}) ``` When a callback arrives: diff --git a/Middleware.md b/Middleware.md index 95bfa67..5654ce3 100644 --- a/Middleware.md +++ b/Middleware.md @@ -200,7 +200,7 @@ admin.AddMiddleware( ### Command-specific validation ```go -ban := admin.NewCommand(banUser, "ban") +ban := admin.Command("ban", banUser) ban.Use(laniakea.NewMiddleware("require-reply", func(ctx *laniakea.MsgContext, db *App) bool { if ctx.Msg == nil || ctx.Msg.ReplyToMessage == nil { ctx.Answer("Reply to a user message first") diff --git a/Migration.md b/Migration.md index 986ddfe..181c877 100644 --- a/Migration.md +++ b/Migration.md @@ -34,7 +34,7 @@ The largest migration points in the current history are `rc.4`, `rc.7`, `rc.10`, ### What changed - `CommandExecutor[T]` changed from `func(ctx *MsgContext, db T)` to `func(ctx *MsgContext, db T) error`. -- `Plugin.NewCommand(...)`, `Plugin.NewPayload(...)`, and `Plugin.AddUpdateHandler(...)` now expect error-returning handlers. +- `Plugin.Command(...)`, `Plugin.Payload(...)`, and `Plugin.AddUpdateHandler(...)` now expect error-returning handlers. - Long plain-text reply helpers were added: `AnswerLong(...)`, `AnswerLongf(...)`, `KeyboardLong(...)`, and `SplitMessageText(...)`. - Message and caption validation now happens before sending Telegram API requests. - Optional strict callback payload decoding was added through `StrictPayloadType`. diff --git a/MsgContext-RU.md b/MsgContext-RU.md index 13a2e4c..f6d4018 100644 --- a/MsgContext-RU.md +++ b/MsgContext-RU.md @@ -97,10 +97,10 @@ English version: [[MsgContext]] В потоке callback особенно полезны: - `EditCallback(...)` -- `AnswerCbQuery()` -- `AnswerCbQueryText(...)` -- `AnswerCbQueryAlert(...)` -- `AnswerCbQueryUrl(...)` +- `AnswerCallback()` +- `AnswerCallbackText(...)` +- `AnswerCallbackAlert(...)` +- `AnswerCallbackURL(...)` - `CallbackDelete()` Они покрывают самые частые callback-сценарии без ручного хождения в `tgapi`. diff --git a/MsgContext.md b/MsgContext.md index 469fc02..7cfa2b1 100644 --- a/MsgContext.md +++ b/MsgContext.md @@ -179,21 +179,21 @@ func approve(ctx *laniakea.MsgContext, db laniakea.NoData) error { } ``` -### `AnswerCbQuery` +### `AnswerCallback` Acknowledges the callback query itself. Use: -- `AnswerCbQuery()` for empty acknowledgement -- `AnswerCbQueryText(...)` for a short notice -- `AnswerCbQueryAlert(...)` for a visible alert -- `AnswerCbQueryUrl(...)` for redirect behavior +- `AnswerCallback()` for empty acknowledgement +- `AnswerCallbackText(...)` for a short notice +- `AnswerCallbackAlert(...)` for a visible alert +- `AnswerCallbackURL(...)` for redirect behavior Example: ```go func approve(ctx *laniakea.MsgContext, db laniakea.NoData) error { - ctx.AnswerCbQueryText("Saved") + ctx.AnswerCallbackText("Saved") ctx.EditCallback("Saved", nil) return nil } @@ -333,7 +333,7 @@ Do not pass raw user input to MarkdownV2 methods without escaping. ### Callback helpers only make sense in callback flow -Methods like `EditCallback(...)` and `AnswerCbQueryText(...)` depend on callback-specific context. +Methods like `EditCallback(...)` and `AnswerCallbackText(...)` depend on callback-specific context. ## A practical example diff --git a/Recipes-RU.md b/Recipes-RU.md index 61a4aeb..3b79702 100644 --- a/Recipes-RU.md +++ b/Recipes-RU.md @@ -19,7 +19,7 @@ English version: [[Recipes]] Комбинируй: - `NewInlineKeyboard(...)` - обработчик данных callback; -- `AnswerCbQuery...`; +- `AnswerCallback...`; - `EditCallback(...)` ### Long reply diff --git a/Recipes.md b/Recipes.md index 27cf2b4..5ce153c 100644 --- a/Recipes.md +++ b/Recipes.md @@ -18,10 +18,10 @@ admin.AddMiddleware(laniakea.NewMiddleware("admin-only", func(ctx *laniakea.MsgC return ctx.From != nil && app.IsAdmin(ctx.From.ID) })) -admin.NewCommand(func(ctx *laniakea.MsgContext, app *App) error { +admin.Command("reload", func(ctx *laniakea.MsgContext, app *App) error { ctx.Answer("Admin command executed") return nil -}, "reload") +}) ``` ## Callback button flow @@ -31,17 +31,17 @@ Use a payload handler for inline keyboard callbacks. ```go menu := laniakea.NewPlugin[laniakea.NoData]("menu") -menu.NewCommand(func(ctx *laniakea.MsgContext, db laniakea.NoData) error { +menu.Command("menu", func(ctx *laniakea.MsgContext, db laniakea.NoData) error { kb := ctx.NewInlineKeyboard(1) kb.AddCallbackButton("Open settings", "settings") ctx.Keyboard("Choose an action", kb) return nil -}, "menu") +}) -menu.NewPayload(func(ctx *laniakea.MsgContext, db laniakea.NoData) error { +menu.Payload("settings", func(ctx *laniakea.MsgContext, db laniakea.NoData) error { ctx.EditCallback("Settings screen", nil) return nil -}, "settings") +}) ``` ## Long plain-text reply @@ -49,11 +49,11 @@ menu.NewPayload(func(ctx *laniakea.MsgContext, db laniakea.NoData) error { Use `AnswerLong(...)` when you want explicit splitting into multiple safe Telegram messages. ```go -plugin.NewCommand(func(ctx *laniakea.MsgContext, db *App) error { +plugin.Command("report", func(ctx *laniakea.MsgContext, db *App) error { report := buildLargePlainTextReport() ctx.AnswerLong(report) return nil -}, "report") +}) ``` If you need an inline keyboard on the final chunk, use `KeyboardLong(...)`. @@ -71,10 +71,10 @@ l10n := laniakea.NewL10n("en"). bot.SetL10n(l10n) -plugin.NewCommand(func(ctx *laniakea.MsgContext, db *App) error { +plugin.Command("start", func(ctx *laniakea.MsgContext, db *App) error { ctx.Answer(ctx.Translate("greeting")) return nil -}, "start") +}) ``` ## Non-command update handler @@ -97,7 +97,7 @@ This is usually cleaner than forcing non-command traffic through a command parse Use drafts when you want to build a reply progressively and publish it once at the end. ```go -plugin.NewCommand(func(ctx *laniakea.MsgContext, db *App) error { +plugin.Command("build", func(ctx *laniakea.MsgContext, db *App) error { draft := ctx.NewDraft() if draft == nil { return nil @@ -111,7 +111,7 @@ plugin.NewCommand(func(ctx *laniakea.MsgContext, db *App) error { } return draft.Flush() -}, "build") +}) ``` ## File upload with `tgapi` @@ -119,15 +119,15 @@ plugin.NewCommand(func(ctx *laniakea.MsgContext, db *App) error { Use the higher-level handler flow for routing, but drop down to `tgapi` uploader methods when you need multipart upload behavior. ```go -plugin.NewCommand(func(ctx *laniakea.MsgContext, db *App) error { - uploader := tgapi.NewUploader(ctx.Api) +plugin.Command("upload", func(ctx *laniakea.MsgContext, db *App) error { + uploader := tgapi.NewUploader(ctx.API) defer uploader.Close() _, err := uploader.SendPhoto(tgapi.UploadPhoto{ ChatID: ctx.Msg.Chat.ID, }, tgapi.NewUploaderFile("report.jpg", []byte("hello"))) return err -}, "upload") +}) ``` If your exact uploader call differs, keep the general rule in mind: handler routing can stay high-level even when the send path needs `tgapi`. diff --git a/Runners-RU.md b/Runners-RU.md index f63d092..6c59067 100644 --- a/Runners-RU.md +++ b/Runners-RU.md @@ -23,9 +23,9 @@ runner := laniakea.NewRunner("cleanup", fn) ``` Потом конфигурируются методы builder: -- `Onetime(bool)` +- `Once(bool)` - `Async(bool)` -- `Timeout(duration)` +- `Every(duration)` ## Основные режимы @@ -51,11 +51,11 @@ runner := laniakea.NewRunner("cleanup", fn) Повторяющийся synchronous runner считается невалидным и пропускается с предупреждением. -Также повторяющийся async runner без `Timeout(...)` пропускается. +Также повторяющийся async runner без `Every(...)` пропускается. ## Когда стартуют фоновые задачи -Фоновые задачи стартуют из `RunWithContext(...)` или `RunWebHookWithContext(...)`, а не из `NewBot(...)`. +Фоновые задачи стартуют из `RunWithContext(...)` или `RunWebhookWithContext(...)`, а не из `NewBot(...)`. Это часть фазы выполнения, а не фазы сборки конфигурации. diff --git a/Runners.md b/Runners.md index 10b97ef..87673e9 100644 --- a/Runners.md +++ b/Runners.md @@ -14,9 +14,9 @@ Each runner is built from: - execution flags configured through builder methods. Main builder methods: -- `Onetime(bool)` +- `Once(bool)` - `Async(bool)` -- `Timeout(duration)` +- `Every(duration)` ## Creating a runner @@ -43,7 +43,7 @@ There are three meaningful configurations. ```go runner := laniakea.NewRunner("warmup", fn). - Onetime(true). + Once(true). Async(false) ``` @@ -58,7 +58,7 @@ Use this for startup work that must complete before the bot is considered ready. ```go runner := laniakea.NewRunner("prefetch", fn). - Onetime(true) + Once(true) ``` Behavior: @@ -72,7 +72,7 @@ Use this for fire-and-forget startup work that is useful but not required before ```go runner := laniakea.NewRunner("cleanup", fn). - Timeout(time.Minute) + Every(time.Minute) ``` Behavior: @@ -86,13 +86,13 @@ Use this for recurring background jobs. One configuration is intentionally treated as invalid: -- `Onetime(false).Async(false)` +- `Once(false).Async(false)` That means: - synchronous repeating runners are skipped; - the bot logs a warning instead of trying to run them inline forever. -Also, repeating async runners with `Timeout(0)` are skipped with a warning. +Also, repeating async runners with `Every(0)` are skipped with a warning. ## Registration @@ -106,7 +106,7 @@ Runners with an empty name are skipped with a warning, so always give them a sta ## Lifecycle -Runners are not started by `NewBot(...)`. They start from `RunWithContext(...)` or `RunWebHookWithContext(...)`, right before the bot begins polling or webhook ingestion. +Runners are not started by `NewBot(...)`. They start from `RunWithContext(...)` or `RunWebhookWithContext(...)`, right before the bot begins polling or webhook ingestion. That means runner execution belongs to the bot's runtime lifecycle, not to its configuration phase. @@ -134,7 +134,7 @@ If a runner must be fatal for startup, make it one-time synchronous and return a ## Shutdown behavior -`RunWithContext(...)` and `RunWebHookWithContext(...)` wait for runner completion in two groups: +`RunWithContext(...)` and `RunWebhookWithContext(...)` wait for runner completion in two groups: - one-time async runners; - background repeating runners. @@ -149,7 +149,7 @@ This means graceful shutdown includes runner shutdown, but only if your runner f ```go cleanup := laniakea.NewRunner("cleanup", func(bot *laniakea.Bot[*App]) error { return bot.GetAppData().CleanupExpired() -}).Timeout(5 * time.Minute) +}).Every(5 * time.Minute) ``` ### Startup warmup @@ -157,7 +157,7 @@ cleanup := laniakea.NewRunner("cleanup", func(bot *laniakea.Bot[*App]) error { ```go warmup := laniakea.NewRunner("warmup", func(bot *laniakea.Bot[*App]) error { return bot.GetAppData().WarmCaches() -}).Onetime(true).Async(false) +}).Once(true).Async(false) ``` ### Background metrics push @@ -165,14 +165,14 @@ warmup := laniakea.NewRunner("warmup", func(bot *laniakea.Bot[*App]) error { ```go metrics := laniakea.NewRunner("metrics", func(bot *laniakea.Bot[*App]) error { return pushMetrics(bot.GetAppData()) -}).Timeout(30 * time.Second) +}).Every(30 * time.Second) ``` ## Recommendations - Use one-time sync runners only for short startup-critical work. - Use repeating async runners for periodic jobs. -- Always set `Timeout(...)` on repeating runners. +- Always set `Every(...)` on repeating runners. - Keep runner bodies small and delegate complex work to regular application services. - Treat runner names as operational identifiers that should make sense in logs. diff --git a/Scenes-RU.md b/Scenes-RU.md index 04c3528..1ba7ec3 100644 --- a/Scenes-RU.md +++ b/Scenes-RU.md @@ -4,7 +4,7 @@ Scenes — это слой маршрутизации Laniakea с сохране ## Что дают сцены -- Регистрацию через `Plugin.NewScene(...)` и `Plugin.AddScene(...)`. +- Регистрацию через `Plugin.Scene(...)` и `Plugin.AddScene(...)`. - Явный вход и выход через `MsgContext.EnterScene(...)`, `EnterSceneStep(...)` и `ExitScene()`. - Области действия сессии на пользователя, чат или пару пользователь-чат. - Обработчики шагов, локальные команды сцены и резервный обработчик сообщений на уровне сцены. @@ -54,7 +54,7 @@ type SessionStore interface { Сцены регистрируются внутри плагина в том же стиле, что и команды с данными callback. ```go -plugin.NewScene("signup"). +plugin.Scene("signup"). SetScope(laniakea.SceneScopeUserChat). SetEntry("ask_name"). OnStep("ask_name", askName). diff --git a/Scenes.md b/Scenes.md index d3698d4..4bdcb97 100644 --- a/Scenes.md +++ b/Scenes.md @@ -4,7 +4,7 @@ Scenes are Laniakea's stateful routing layer for multi-step and modal bot flows. ## What scenes give you -- Scene registration through `Plugin.NewScene(...)` and `Plugin.AddScene(...)`. +- Scene registration through `Plugin.Scene(...)` and `Plugin.AddScene(...)`. - Explicit entry and exit through `MsgContext.EnterScene(...)`, `EnterSceneStep(...)`, and `ExitScene()`. - Per-user, per-chat, or per-user-chat session scopes. - Step handlers, scene-local commands, and a scene-level message fallback. @@ -54,7 +54,7 @@ Recommended default: Scenes are registered inside plugins in the same style as commands and payloads. ```go -plugin.NewScene("signup"). +plugin.Scene("signup"). SetScope(laniakea.SceneScopeUserChat). SetEntry("ask_name"). OnStep("ask_name", askName). diff --git a/Semver-and-Releases.md b/Semver-and-Releases.md index 53ffaeb..51dac20 100644 --- a/Semver-and-Releases.md +++ b/Semver-and-Releases.md @@ -53,7 +53,7 @@ In broad terms: - patch version: backward-compatible fixes and clarifications; - `-rc.N`: prerelease iteration before the stable release line. -The current code declares its version in [utils/version.go](/home/scuro/projects/Laniakea/utils/version.go): +The current code declares its version in `utils/version.go`: - `VersionString` - `VersionMajor` - `VersionMinor` diff --git a/Update-Routing-Model-RU.md b/Update-Routing-Model-RU.md index f297047..3db0ff1 100644 --- a/Update-Routing-Model-RU.md +++ b/Update-Routing-Model-RU.md @@ -10,12 +10,12 @@ English version: [[Update-Routing-Model]] Сейчас Laniakea маршрутизирует обновления так: -| Update type | Routing path | -|---|---| -| `message` | command flow | -| `channel_post` | command flow | -| `callback_query` | payload flow | -| всё остальное | `AddUpdateHandler(...)` | +| Update type | Routing path | +|------------------|-------------------------| +| `message` | command flow | +| `channel_post` | command flow | +| `callback_query` | payload flow | +| всё остальное | `AddUpdateHandler(...)` | Важно: - `message`, `channel_post` и `callback_query` зарезервированы и не должны регистрироваться через `AddUpdateHandler(...)`. @@ -65,7 +65,7 @@ Payload flow применяется только к: Базовые гарантии: - `ctx.Update` всегда заполнен -- `ctx.CallbackQueryId` заполнен +- `ctx.CallbackQueryID` заполнен - `ctx.From` и `ctx.FromID` заполняются, если Telegram прислал user - `ctx.Args` заполняется из декодированных данных callback - `ctx.Logger` переключается на logger совпавшего плагина, если он задан @@ -80,15 +80,15 @@ Payload flow применяется только к: Гарантии: - `ctx.Msg` заполнен -- `ctx.CallbackMsgId` заполнен -- `ctx.InlineMsgId == ""` +- `ctx.CallbackMsgID` заполнен +- `ctx.InlineMsgID == ""` ### Callback query, привязанный к inline message Гарантии: - `ctx.Msg == nil` -- `ctx.CallbackMsgId == 0` -- `ctx.InlineMsgId` заполнен +- `ctx.CallbackMsgID == 0` +- `ctx.InlineMsgID` заполнен ## Контракт generic update handlers @@ -110,14 +110,14 @@ Generic update handlers регистрируются через `Plugin.AddUpdat ### Update types, которые несут message -| Update type | `ctx.Msg` | `ctx.From` / `ctx.FromID` | -|---|---|---| -| `message` | да | да, если есть `Msg.From` | -| `edited_message` | да | да, если есть `Msg.From` | -| `channel_post` | да | только если есть `Msg.From` | -| `edited_channel_post` | да | только если есть `Msg.From` | -| `business_message` | да | да, если есть `Msg.From` | -| `edited_business_message` | да | да, если есть `Msg.From` | +| Update type | `ctx.Msg` | `ctx.From` / `ctx.FromID` | +|---------------------------|-----------|-----------------------------| +| `message` | да | да, если есть `Msg.From` | +| `edited_message` | да | да, если есть `Msg.From` | +| `channel_post` | да | только если есть `Msg.From` | +| `edited_channel_post` | да | только если есть `Msg.From` | +| `business_message` | да | да, если есть `Msg.From` | +| `edited_business_message` | да | да, если есть `Msg.From` | Важно: - message-backed не означает command-routed @@ -125,37 +125,37 @@ Generic update handlers регистрируются через `Plugin.AddUpdat ### Update types с user, но без message -| Update type | `ctx.Msg` | `ctx.From` / `ctx.FromID` | -|---|---|---| -| `inline_query` | нет | да | -| `chosen_inline_result` | нет | да | -| `shipping_query` | нет | да | -| `pre_checkout_query` | нет | да | -| `purchased_paid_media` | нет | да | -| `my_chat_member` | нет | да | -| `chat_member` | нет | да | -| `chat_join_request` | нет | да | -| `business_connection` | нет | да | -| `poll_answer` | нет | да | -| `message_reaction` | нет | да, если Telegram прислал `User` | -| `chat_boost` | нет | да | -| `removed_chat_boost` | нет | да | +| Update type | `ctx.Msg` | `ctx.From` / `ctx.FromID` | +|------------------------|-----------|----------------------------------| +| `inline_query` | нет | да | +| `chosen_inline_result` | нет | да | +| `shipping_query` | нет | да | +| `pre_checkout_query` | нет | да | +| `purchased_paid_media` | нет | да | +| `my_chat_member` | нет | да | +| `chat_member` | нет | да | +| `chat_join_request` | нет | да | +| `business_connection` | нет | да | +| `poll_answer` | нет | да | +| `message_reaction` | нет | да, если Telegram прислал `User` | +| `chat_boost` | нет | да | +| `removed_chat_boost` | нет | да | ### Callback-specific update type -| Update type | `ctx.Msg` | `ctx.From` / `ctx.FromID` | Спецполя | -|---|---|---|---| -| `callback_query` с `Message` | да | да | `CallbackQueryId`, `CallbackMsgId` | -| `callback_query` с `InlineMessageID` | нет | да | `CallbackQueryId`, `InlineMsgId` | +| Update type | `ctx.Msg` | `ctx.From` / `ctx.FromID` | Спецполя | +|--------------------------------------|-----------|---------------------------|------------------------------------| +| `callback_query` с `Message` | да | да | `CallbackQueryID`, `CallbackMsgID` | +| `callback_query` с `InlineMessageID` | нет | да | `CallbackQueryID`, `InlineMsgID` | ### Update types без нормализованных гарантий по user/message -| Update type | `ctx.Msg` | `ctx.From` / `ctx.FromID` | -|---|---|---| -| `poll` | нет | нет | -| `message_reaction_count` | нет | нет | -| `deleted_business_messages` | нет | нет | -| `unknown` | нет | нет | +| Update type | `ctx.Msg` | `ctx.From` / `ctx.FromID` | +|-----------------------------|-----------|---------------------------| +| `poll` | нет | нет | +| `message_reaction_count` | нет | нет | +| `deleted_business_messages` | нет | нет | +| `unknown` | нет | нет | ## Что уже достаточно стабильно diff --git a/Update-Routing-Model.md b/Update-Routing-Model.md index c66a2d0..ced99af 100644 --- a/Update-Routing-Model.md +++ b/Update-Routing-Model.md @@ -10,12 +10,12 @@ This is a model of current framework behavior, not a second API surface. The goa Laniakea currently routes updates like this: -| Update type | Routing path | -|---|---| -| `message` | command flow | -| `channel_post` | command flow | -| `callback_query` | payload flow | -| everything else | `AddUpdateHandler(...)` | +| Update type | Routing path | +|------------------|-------------------------| +| `message` | command flow | +| `channel_post` | command flow | +| `callback_query` | payload flow | +| everything else | `AddUpdateHandler(...)` | Important: - `message`, `channel_post`, and `callback_query` are reserved from `AddUpdateHandler(...)`. @@ -65,7 +65,7 @@ Payload flow applies only to: Base guarantees: - `ctx.Update` is always present -- `ctx.CallbackQueryId` is populated +- `ctx.CallbackQueryID` is populated - `ctx.From` and `ctx.FromID` are populated when Telegram includes a user - `ctx.Args` is populated from decoded payload args - `ctx.Logger` switches to the matched plugin logger when one exists @@ -80,15 +80,15 @@ There are two payload target shapes. Guarantees: - `ctx.Msg` is present -- `ctx.CallbackMsgId` is populated -- `ctx.InlineMsgId == ""` +- `ctx.CallbackMsgID` is populated +- `ctx.InlineMsgID == ""` ### Callback query targeting an inline message Guarantees: - `ctx.Msg == nil` -- `ctx.CallbackMsgId == 0` -- `ctx.InlineMsgId` is populated +- `ctx.CallbackMsgID == 0` +- `ctx.InlineMsgID` is populated ## Generic update handler contract @@ -110,14 +110,14 @@ Important limitation: ### Message-backed update kinds -| Update type | `ctx.Msg` | `ctx.From` / `ctx.FromID` | -|---|---|---| -| `message` | yes | yes, when `Msg.From` exists | -| `edited_message` | yes | yes, when `Msg.From` exists | -| `channel_post` | yes | only when `Msg.From` exists | -| `edited_channel_post` | yes | only when `Msg.From` exists | -| `business_message` | yes | yes, when `Msg.From` exists | -| `edited_business_message` | yes | yes, when `Msg.From` exists | +| Update type | `ctx.Msg` | `ctx.From` / `ctx.FromID` | +|---------------------------|-----------|-----------------------------| +| `message` | yes | yes, when `Msg.From` exists | +| `edited_message` | yes | yes, when `Msg.From` exists | +| `channel_post` | yes | only when `Msg.From` exists | +| `edited_channel_post` | yes | only when `Msg.From` exists | +| `business_message` | yes | yes, when `Msg.From` exists | +| `edited_business_message` | yes | yes, when `Msg.From` exists | Important: - message-backed does **not** mean command-routed @@ -125,37 +125,37 @@ Important: ### User-backed but not message-backed update kinds -| Update type | `ctx.Msg` | `ctx.From` / `ctx.FromID` | -|---|---|---| -| `inline_query` | no | yes | -| `chosen_inline_result` | no | yes | -| `shipping_query` | no | yes | -| `pre_checkout_query` | no | yes | -| `purchased_paid_media` | no | yes | -| `my_chat_member` | no | yes | -| `chat_member` | no | yes | -| `chat_join_request` | no | yes | -| `business_connection` | no | yes | -| `poll_answer` | no | yes | -| `message_reaction` | no | yes, when Telegram includes `User` | -| `chat_boost` | no | yes | -| `removed_chat_boost` | no | yes | +| Update type | `ctx.Msg` | `ctx.From` / `ctx.FromID` | +|------------------------|-----------|------------------------------------| +| `inline_query` | no | yes | +| `chosen_inline_result` | no | yes | +| `shipping_query` | no | yes | +| `pre_checkout_query` | no | yes | +| `purchased_paid_media` | no | yes | +| `my_chat_member` | no | yes | +| `chat_member` | no | yes | +| `chat_join_request` | no | yes | +| `business_connection` | no | yes | +| `poll_answer` | no | yes | +| `message_reaction` | no | yes, when Telegram includes `User` | +| `chat_boost` | no | yes | +| `removed_chat_boost` | no | yes | ### Callback-specific update kind -| Update type | `ctx.Msg` | `ctx.From` / `ctx.FromID` | Special fields | -|---|---|---|---| -| `callback_query` with `Message` | yes | yes | `CallbackQueryId`, `CallbackMsgId` | -| `callback_query` with `InlineMessageID` | no | yes | `CallbackQueryId`, `InlineMsgId` | +| Update type | `ctx.Msg` | `ctx.From` / `ctx.FromID` | Special fields | +|-----------------------------------------|-----------|---------------------------|------------------------------------| +| `callback_query` with `Message` | yes | yes | `CallbackQueryID`, `CallbackMsgID` | +| `callback_query` with `InlineMessageID` | no | yes | `CallbackQueryID`, `InlineMsgID` | ### Update kinds without normalized user/message guarantees -| Update type | `ctx.Msg` | `ctx.From` / `ctx.FromID` | -|---|---|---| -| `poll` | no | no | -| `message_reaction_count` | no | no | -| `deleted_business_messages` | no | no | -| `unknown` | no | no | +| Update type | `ctx.Msg` | `ctx.From` / `ctx.FromID` | +|-----------------------------|-----------|---------------------------| +| `poll` | no | no | +| `message_reaction_count` | no | no | +| `deleted_business_messages` | no | no | +| `unknown` | no | no | ## What is already stable enough to rely on diff --git a/Webhook-Runtime-RU.md b/Webhook-Runtime-RU.md index 5cd534e..7774185 100644 --- a/Webhook-Runtime-RU.md +++ b/Webhook-Runtime-RU.md @@ -2,11 +2,11 @@ English version: [[Webhook-Runtime]] -Эта страница объясняет bot-level webhook runtime в Laniakea: как работает `RunWebHookWithContext(...)`, чем он отличается от низкоуровневых webhook-вызовов в `tgapi` и какие runtime-гарантии он делит с polling-режимом. +Эта страница объясняет bot-level webhook runtime в Laniakea: как работает `RunWebhookWithContext(...)`, чем он отличается от низкоуровневых webhook-вызовов в `tgapi` и какие runtime-гарантии он делит с polling-режимом. Важно про naming: -- текущий публичный API использует историческое написание `WebHook` в идентификаторах вроде `RunWebHookWithContext(...)`, `RunWebHook(...)` и `BotWebHookOpts`; -- в тексте страницы используется обычное слово "webhook", но в примерах остаются реальные имена Go API. +- публичный API использует идиоматичное написание `Webhook` в идентификаторах вроде `RunWebhookWithContext(...)`, `RunWebhook(...)` и `BotWebhookOpts`; +- в примерах остаются реальные имена Go API. ## Когда использовать webhook runtime @@ -25,11 +25,11 @@ English version: [[Webhook-Runtime]] ## Точки входа Главные bot-level точки входа такие: -- `RunWebHookWithContext(ctx, opts, tlsFiles...)` -- `RunWebHook(opts, tlsFiles...)` -- `NewBotWebHookOpts()` +- `RunWebhookWithContext(ctx, opts, tlsFiles...)` +- `RunWebhook(opts, tlsFiles...)` +- `NewBotWebhookOpts()` -`RunWebHook(...)` — это просто короткая форма для `RunWebHookWithContext(context.Background(), ...)`. +`RunWebhook(...)` — это просто короткая форма для `RunWebhookWithContext(context.Background(), ...)`. Обычный шаблон выглядит так: @@ -46,20 +46,20 @@ defer bot.Close() bot.SetAppData(app) bot.AddPlugins(plugin) -webhookOpts := laniakea.NewBotWebHookOpts(). +webhookOpts := laniakea.NewBotWebhookOpts(). SetURL("https://bot.example.com"). SetPath("/telegram"). SetLocalPort(8080). SetSecretToken("shared-secret") -if err := bot.RunWebHookWithContext(ctx, webhookOpts); err != nil { +if err := bot.RunWebhookWithContext(ctx, webhookOpts); err != nil { return err } ``` ## Что именно берет на себя bot-level runtime -`RunWebHookWithContext(...)` — это не просто обертка над Telegram `setWebhook`. +`RunWebhookWithContext(...)` — это не просто обертка над Telegram `setWebhook`. Он: - валидирует bot-level условия старта, например prefixes и наличие зарегистрированных plugins; @@ -86,7 +86,7 @@ Webhook runtime использует те же основные гарантии ## Основные webhook options -`BotWebHookOpts` управляет и регистрацией webhook у Telegram, и локальным HTTP server. +`BotWebhookOpts` управляет и регистрацией webhook у Telegram, и локальным HTTP server. Поля, которые важны в первую очередь: @@ -167,12 +167,12 @@ Webhook runtime использует те же основные гарантии ## Поведение HTTP и TLS -По умолчанию `RunWebHookWithContext(...)` поднимает обычный HTTP server на `LocalPort`. +По умолчанию `RunWebhookWithContext(...)` поднимает обычный HTTP server на `LocalPort`. Если передать два TLS-файла, локально стартует HTTPS. Важно: -- текущий публичный API ожидает существующий порядок аргументов `key, cert` при вызове `RunWebHookWithContext(...)`; +- текущий публичный API ожидает существующий порядок аргументов `key, cert` при вызове `RunWebhookWithContext(...)`; - это отличается от более привычной ментальной модели `cert, key`, которую многие Go-разработчики ожидают от `ListenAndServeTLS`. Поэтому в реальном setup лучше писать этот вызов максимально явно. @@ -202,7 +202,7 @@ Webhook runtime использует те же основные гарантии - `URL` — это то, что видит Telegram; - `Path` и `LocalPort` — это то, что реально обслуживает твой бот; - в production эти значения часто относятся к разным слоям инфраструктуры. -- если ты переводишь работающий deployment с webhook-режима на polling, сначала удали webhook через `CloseWebHook()` или `tgapi.DeleteWebhook(...)`; Telegram не прекращает webhook-доставку автоматически. +- если ты переводишь работающий deployment с webhook-режима на polling, сначала удали webhook через `CloseWebhook()` или `tgapi.DeleteWebhook(...)`; Telegram не прекращает webhook-доставку автоматически. ## Связь с webhook methods в `tgapi` @@ -221,7 +221,7 @@ Webhook runtime использует те же основные гарантии когда тебе нужна собственная инфраструктура вокруг webhook path и ты не хочешь, чтобы сам бот владел HTTP server. То есть: -- `RunWebHookWithContext(...)` — это framework runtime API; +- `RunWebhookWithContext(...)` — это framework runtime API; - webhook methods из `tgapi` — это низкоуровневые transport primitives. ## Частые ошибки diff --git a/Webhook-Runtime.md b/Webhook-Runtime.md index 9b168f0..045f34b 100644 --- a/Webhook-Runtime.md +++ b/Webhook-Runtime.md @@ -2,11 +2,11 @@ Russian version: [[Webhook-Runtime-RU]] -This page explains the bot-level webhook runtime in Laniakea: how `RunWebHookWithContext(...)` works, what it owns, how it differs from low-level `tgapi` webhook calls, and what runtime guarantees it shares with polling mode. +This page explains the bot-level webhook runtime in Laniakea: how `RunWebhookWithContext(...)` works, what it owns, how it differs from low-level `tgapi` webhook calls, and what runtime guarantees it shares with polling mode. Important naming note: -- the current public API uses the historical `WebHook` spelling in identifiers such as `RunWebHookWithContext(...)`, `RunWebHook(...)`, and `BotWebHookOpts`; -- this page uses the more common English term "webhook" for readability, but examples keep the actual Go API names. +- the public API uses idiomatic `Webhook` spelling in identifiers such as `RunWebhookWithContext(...)`, `RunWebhook(...)`, and `BotWebhookOpts`; +- examples keep the actual Go API names. ## When to use webhook runtime @@ -25,11 +25,11 @@ Use polling when: ## Entry points The main bot-level entry points are: -- `RunWebHookWithContext(ctx, opts, tlsFiles...)` -- `RunWebHook(opts, tlsFiles...)` -- `NewBotWebHookOpts()` +- `RunWebhookWithContext(ctx, opts, tlsFiles...)` +- `RunWebhook(opts, tlsFiles...)` +- `NewBotWebhookOpts()` -`RunWebHook(...)` is only a shorthand for `RunWebHookWithContext(context.Background(), ...)`. +`RunWebhook(...)` is only a shorthand for `RunWebhookWithContext(context.Background(), ...)`. The usual pattern looks like: @@ -46,20 +46,20 @@ defer bot.Close() bot.SetAppData(app) bot.AddPlugins(plugin) -webhookOpts := laniakea.NewBotWebHookOpts(). +webhookOpts := laniakea.NewBotWebhookOpts(). SetURL("https://bot.example.com"). SetPath("/telegram"). SetLocalPort(8080). SetSecretToken("shared-secret") -if err := bot.RunWebHookWithContext(ctx, webhookOpts); err != nil { +if err := bot.RunWebhookWithContext(ctx, webhookOpts); err != nil { return err } ``` ## What the bot-level runtime owns -`RunWebHookWithContext(...)` is more than a wrapper around Telegram's `setWebhook`. +`RunWebhookWithContext(...)` is more than a wrapper around Telegram's `setWebhook`. It: - validates bot startup preconditions such as prefixes and registered plugins; @@ -86,7 +86,7 @@ If you already understand [[Bot-Lifecycle]], the webhook mode should feel like a ## Main webhook options -`BotWebHookOpts` controls both Telegram webhook registration and the local server behavior. +`BotWebhookOpts` controls both Telegram webhook registration and the local server behavior. Fields you will care about first: @@ -167,12 +167,12 @@ Use this only when you specifically need Telegram's `ip_address` webhook option. ## HTTP and TLS behavior -By default, `RunWebHookWithContext(...)` starts a plain HTTP server on `LocalPort`. +By default, `RunWebhookWithContext(...)` starts a plain HTTP server on `LocalPort`. If you pass two TLS files, it starts HTTPS locally instead. Important: -- the current public API expects the existing key-then-cert argument order when calling `RunWebHookWithContext(...)`; +- the current public API expects the existing key-then-cert argument order when calling `RunWebhookWithContext(...)`; - that differs from the more common `cert, key` mental model many Go developers expect from `ListenAndServeTLS`. Be explicit in your own setup code so this does not become a deployment footgun. @@ -202,7 +202,7 @@ Also keep in mind: - `URL` is what Telegram sees; - `Path` and `LocalPort` are what your bot actually serves; - these are often not the same thing in production. -- if you switch a running deployment from webhook mode to polling, delete the webhook first with `CloseWebHook()` or `tgapi.DeleteWebhook(...)`; Telegram does not stop webhook delivery automatically. +- if you switch a running deployment from webhook mode to polling, delete the webhook first with `CloseWebhook()` or `tgapi.DeleteWebhook(...)`; Telegram does not stop webhook delivery automatically. ## Relation to `tgapi` webhook methods @@ -221,7 +221,7 @@ Use lower-level `tgapi` calls such as: when you need custom infrastructure around the webhook path and do not want the bot to own the HTTP server itself. In other words: -- `RunWebHookWithContext(...)` is the framework runtime API; +- `RunWebhookWithContext(...)` is the framework runtime API; - `tgapi` webhook methods are the lower-level transport primitives. ## Common mistakes diff --git a/tgapi-Overview.md b/tgapi-Overview.md index 4a6009a..606f22e 100644 --- a/tgapi-Overview.md +++ b/tgapi-Overview.md @@ -82,7 +82,7 @@ Use the context-aware variants when cancellation, deadlines, or graceful shutdow Useful options include: - `SetHTTPClient(...)` - `UseTestServer(...)` -- `SetAPIUrl(...)` +- `SetAPIURL(...)` - `SetLimiter(...)` - `SetLimiterDrop(...)`