REPOSITORY / ScuroNeko/Laniakea
Wiki
(doc): MessageContext rename, webhook casing, Runner API, v1.0.0 migration
- MsgContext → MessageContext across all pages - RunWebHookWithContext → RunWebhookWithContext (and CloseWebhook, BotWebhookOpts) across all pages - Runners.md + Runners-RU.md: replace Onetime/Timeout with Every/Async, rewrite examples - Migration.md: add v1.0.0 migration section covering all breaking changes
+6
-6
@@ -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()`: локальные ресурсы всё равно нужно закрывать отдельно.
|
||||
|
||||
## Частые ошибки
|
||||
|
||||
|
||||
+10
-10
@@ -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.
|
||||
@@ -43,7 +43,7 @@ The normal pattern is to finish all structural configuration before starting the
|
||||
- `SetDraftProvider(...)`, `SetSessionStore(...)`, and `SetSceneScopePriority(...)` replace runtime helpers.
|
||||
- `SetErrorTemplate(...)` adjusts centralized user-facing error text.
|
||||
|
||||
For an overview of handlers and plugins, see [[Commands-and-Plugins]]. For context helpers available inside handlers, see [[MsgContext]].
|
||||
For an overview of handlers and plugins, see [[Commands-and-Plugins]]. For context helpers available inside handlers, see [[MessageContext]].
|
||||
|
||||
## `AddPlugins(...)` is a configuration commit point
|
||||
|
||||
@@ -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,7 +97,7 @@ if err != nil {
|
||||
defer bot.Close()
|
||||
|
||||
plugin := laniakea.NewPlugin[laniakea.NoData]("main")
|
||||
plugin.NewCommand(func(ctx *laniakea.MsgContext, db laniakea.NoData) error {
|
||||
plugin.NewCommand(func(ctx *laniakea.MessageContext, db laniakea.NoData) error {
|
||||
ctx.Answer("pong")
|
||||
return nil
|
||||
}, "ping")
|
||||
@@ -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;
|
||||
|
||||
@@ -47,11 +47,11 @@ plugin := laniakea.NewPlugin[laniakea.NoData]("admin")
|
||||
Сигнатура обработчика команды такая:
|
||||
|
||||
```go
|
||||
func(ctx *laniakea.MsgContext, db T) error
|
||||
func(ctx *laniakea.MessageContext, db T) error
|
||||
```
|
||||
|
||||
Где:
|
||||
- `ctx` — текущий `MsgContext`;
|
||||
- `ctx` — текущий `MessageContext`;
|
||||
- `db` — значение generic-параметра `T`, которое ты передал в `Bot`.
|
||||
|
||||
Возвращай:
|
||||
@@ -61,7 +61,7 @@ func(ctx *laniakea.MsgContext, db T) error
|
||||
Пример:
|
||||
|
||||
```go
|
||||
func start(ctx *laniakea.MsgContext, db *App) error {
|
||||
func start(ctx *laniakea.MessageContext, db *App) error {
|
||||
ctx.Answer("Welcome")
|
||||
return nil
|
||||
}
|
||||
@@ -125,7 +125,7 @@ plugin.AddCommand(
|
||||
Пример:
|
||||
|
||||
```go
|
||||
func confirmDelete(ctx *laniakea.MsgContext, db *App) error {
|
||||
func confirmDelete(ctx *laniakea.MessageContext, db *App) error {
|
||||
ctx.EditCallback("Deleted", nil)
|
||||
return nil
|
||||
}
|
||||
@@ -147,7 +147,7 @@ plugin.AddPayload(plugin.NewPayload(confirmDelete, "delete.confirm"))
|
||||
Пример:
|
||||
|
||||
```go
|
||||
plugin.AddUpdateHandler(tgapi.UpdateTypeInlineQuery, func(ctx *laniakea.MsgContext, db *App) error {
|
||||
plugin.AddUpdateHandler(tgapi.UpdateTypeInlineQuery, func(ctx *laniakea.MessageContext, db *App) error {
|
||||
return nil
|
||||
})
|
||||
```
|
||||
@@ -170,7 +170,7 @@ plugin.AddUpdateHandler(tgapi.UpdateTypeInlineQuery, func(ctx *laniakea.MsgConte
|
||||
Для текстовой команды поток примерно такой:
|
||||
|
||||
1. Приходит Telegram update.
|
||||
2. `Bot` готовит `MsgContext`.
|
||||
2. `Bot` готовит `MessageContext`.
|
||||
3. Выполняется middleware бота.
|
||||
4. Находится подходящий плагин.
|
||||
5. Выполняется middleware плагина.
|
||||
@@ -258,5 +258,5 @@ return nil
|
||||
- [[Getting-Started-RU]]
|
||||
- [[Bot-Lifecycle-RU]]
|
||||
- [[Commands-and-Plugins]]
|
||||
- [[MsgContext]]
|
||||
- [[MessageContext]]
|
||||
- [[Inline-Keyboards-and-Payloads]]
|
||||
|
||||
+10
-10
@@ -49,7 +49,7 @@ That keeps command registration and middleware ownership clear.
|
||||
The command handler signature is:
|
||||
|
||||
```go
|
||||
func(ctx *laniakea.MsgContext, db T) error
|
||||
func(ctx *laniakea.MessageContext, db T) error
|
||||
```
|
||||
|
||||
Where:
|
||||
@@ -63,7 +63,7 @@ Return:
|
||||
Example:
|
||||
|
||||
```go
|
||||
func start(ctx *laniakea.MsgContext, db *App) error {
|
||||
func start(ctx *laniakea.MessageContext, db *App) error {
|
||||
ctx.Answer("Welcome")
|
||||
return nil
|
||||
}
|
||||
@@ -89,7 +89,7 @@ So:
|
||||
## The easiest command example
|
||||
|
||||
```go
|
||||
func echo(ctx *laniakea.MsgContext, db laniakea.NoData) error {
|
||||
func echo(ctx *laniakea.MessageContext, db laniakea.NoData) error {
|
||||
if ctx.Text == "" {
|
||||
ctx.Answer("Usage: /echo <text>")
|
||||
return nil
|
||||
@@ -136,7 +136,7 @@ Payload handlers are for callback data coming from inline keyboard buttons.
|
||||
Register them with `NewPayload(...)` or `AddPayload(...)`:
|
||||
|
||||
```go
|
||||
func confirmDelete(ctx *laniakea.MsgContext, db *App) error {
|
||||
func confirmDelete(ctx *laniakea.MessageContext, db *App) error {
|
||||
ctx.EditCallback("Deleted", nil)
|
||||
return nil
|
||||
}
|
||||
@@ -158,7 +158,7 @@ Update handlers are for Telegram updates outside the normal command/payload flow
|
||||
Register them with `AddUpdateHandler(...)`:
|
||||
|
||||
```go
|
||||
plugin.AddUpdateHandler(tgapi.UpdateTypeInlineQuery, func(ctx *laniakea.MsgContext, db *App) error {
|
||||
plugin.AddUpdateHandler(tgapi.UpdateTypeInlineQuery, func(ctx *laniakea.MessageContext, db *App) error {
|
||||
// handle inline query here
|
||||
return nil
|
||||
})
|
||||
@@ -183,7 +183,7 @@ stay on the command/payload flow and are not meant to be registered through `Add
|
||||
For text commands:
|
||||
|
||||
1. Telegram update arrives
|
||||
2. bot prepares `MsgContext`
|
||||
2. bot prepares `MessageContext`
|
||||
3. bot middleware runs
|
||||
4. matching plugin is found
|
||||
5. plugin middleware runs
|
||||
@@ -235,7 +235,7 @@ Avoid one giant plugin for the entire bot unless the bot is very small.
|
||||
|
||||
```go
|
||||
admin := laniakea.NewPlugin[*App]("admin")
|
||||
admin.AddMiddleware(laniakea.NewMiddleware("admin-only", func(ctx *laniakea.MsgContext, app *App) bool {
|
||||
admin.AddMiddleware(laniakea.NewMiddleware("admin-only", func(ctx *laniakea.MessageContext, app *App) bool {
|
||||
if !app.IsAdmin(ctx.FromID) {
|
||||
ctx.Answer("Access denied")
|
||||
return false
|
||||
@@ -243,7 +243,7 @@ admin.AddMiddleware(laniakea.NewMiddleware("admin-only", func(ctx *laniakea.MsgC
|
||||
return true
|
||||
}))
|
||||
|
||||
admin.AddCommand(admin.NewCommand(func(ctx *laniakea.MsgContext, app *App) error {
|
||||
admin.AddCommand(admin.NewCommand(func(ctx *laniakea.MessageContext, app *App) error {
|
||||
ctx.Answer("Banned")
|
||||
return nil
|
||||
}, "ban"))
|
||||
@@ -252,7 +252,7 @@ admin.AddCommand(admin.NewCommand(func(ctx *laniakea.MsgContext, app *App) error
|
||||
### Example: payload handler for inline keyboard callback
|
||||
|
||||
```go
|
||||
plugin.AddPayload(plugin.NewPayload(func(ctx *laniakea.MsgContext, app *App) error {
|
||||
plugin.AddPayload(plugin.NewPayload(func(ctx *laniakea.MessageContext, app *App) error {
|
||||
ctx.AnswerCbQueryText("Accepted")
|
||||
ctx.EditCallback("Done", nil)
|
||||
return nil
|
||||
@@ -304,6 +304,6 @@ Use:
|
||||
- command middleware for narrow, local checks
|
||||
|
||||
## Where to go next
|
||||
- Read [[MsgContext]] next to understand what handlers can do once they are triggered.
|
||||
- Read [[MessageContext]] next to understand what handlers can do once they are triggered.
|
||||
- Read [[Inline-Keyboards-and-Payloads]] if you are starting to use buttons and callback data.
|
||||
- Read [[Middleware]] for execution-order and async details.
|
||||
|
||||
+1
-1
@@ -75,6 +75,6 @@ draft.Flush()
|
||||
|
||||
## Что читать дальше
|
||||
|
||||
- [[MsgContext-RU]]
|
||||
- [[MessageContext-RU]]
|
||||
- [[Bot-Lifecycle-RU]]
|
||||
- [[Drafts]]
|
||||
|
||||
+4
-4
@@ -23,9 +23,9 @@ The draft system has two layers:
|
||||
|
||||
The provider is safe for concurrent use. Individual drafts are intended for single-goroutine use unless you add your own synchronization.
|
||||
|
||||
## The easiest entry point: `MsgContext.NewDraft()`
|
||||
## The easiest entry point: `MessageContext.NewDraft()`
|
||||
|
||||
Inside a handler, the usual entry point is `MsgContext.NewDraft()` or `MsgContext.NewDraftMarkdown()`.
|
||||
Inside a handler, the usual entry point is `MessageContext.NewDraft()` or `MessageContext.NewDraftMarkdown()`.
|
||||
|
||||
Those helpers:
|
||||
- create a draft from the bot's configured `DraftProvider`;
|
||||
@@ -35,7 +35,7 @@ Those helpers:
|
||||
Typical usage:
|
||||
|
||||
```go
|
||||
func report(ctx *laniakea.MsgContext, db *App) error {
|
||||
func report(ctx *laniakea.MessageContext, db *App) error {
|
||||
draft := ctx.NewDraft()
|
||||
if draft == nil {
|
||||
return nil
|
||||
@@ -174,5 +174,5 @@ Prefer direct reply helpers such as `Answer(...)` or `AnswerLong(...)` when:
|
||||
|
||||
## Related pages
|
||||
|
||||
- [[MsgContext]] for handler-scoped reply and draft helpers
|
||||
- [[MessageContext]] for handler-scoped reply and draft helpers
|
||||
- [[Bot-Lifecycle]] for draft-provider attachment through the bot
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@ English version: [[Error-Handling]]
|
||||
## Как это выглядит
|
||||
|
||||
```go
|
||||
func ping(ctx *laniakea.MsgContext, db *App) error {
|
||||
func ping(ctx *laniakea.MessageContext, db *App) error {
|
||||
ctx.Answer("pong")
|
||||
return nil
|
||||
}
|
||||
|
||||
+4
-4
@@ -16,7 +16,7 @@ That applies to:
|
||||
The basic pattern is:
|
||||
|
||||
```go
|
||||
func ping(ctx *laniakea.MsgContext, db *App) error {
|
||||
func ping(ctx *laniakea.MessageContext, db *App) error {
|
||||
ctx.Answer("pong")
|
||||
return nil
|
||||
}
|
||||
@@ -183,7 +183,7 @@ Related page:
|
||||
### Centralized command failure
|
||||
|
||||
```go
|
||||
plugin.NewCommand(func(ctx *laniakea.MsgContext, db *App) error {
|
||||
plugin.NewCommand(func(ctx *laniakea.MessageContext, db *App) error {
|
||||
result, err := db.DoWork()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to build report: %w", err)
|
||||
@@ -196,7 +196,7 @@ plugin.NewCommand(func(ctx *laniakea.MsgContext, db *App) error {
|
||||
### Manual denial response
|
||||
|
||||
```go
|
||||
plugin.NewCommand(func(ctx *laniakea.MsgContext, db *App) error {
|
||||
plugin.NewCommand(func(ctx *laniakea.MessageContext, db *App) error {
|
||||
if ctx.From == nil || !db.Allowed(ctx.From.ID) {
|
||||
ctx.Answer("Access denied")
|
||||
return nil
|
||||
@@ -208,7 +208,7 @@ plugin.NewCommand(func(ctx *laniakea.MsgContext, db *App) error {
|
||||
### Callback-specific manual alert
|
||||
|
||||
```go
|
||||
plugin.NewPayload(func(ctx *laniakea.MsgContext, db *App) error {
|
||||
plugin.NewPayload(func(ctx *laniakea.MessageContext, db *App) error {
|
||||
if !ready {
|
||||
ctx.AnswerCbQueryAlert("This action is not available yet")
|
||||
return nil
|
||||
|
||||
+22
-22
@@ -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.
|
||||
|
||||
Практическая цель:
|
||||
@@ -65,7 +65,7 @@
|
||||
|
||||
Текущее состояние:
|
||||
|
||||
- Во фреймворке теперь есть `Policy[T]` как явная переиспользуемая модель правила доступа, работающая поверх нормализованного `MsgContext` и общих данных приложения.
|
||||
- Во фреймворке теперь есть `Policy[T]` как явная переиспользуемая модель правила доступа, работающая поверх нормализованного `MessageContext` и общих данных приложения.
|
||||
- Политики интегрируются в уже существующую модель выполнения через `RequirePolicy(...)`, поэтому авторизация остаётся на middleware-пути и не создаёт второй pipeline маршрутизации.
|
||||
- У бота и плагинов появились явные helpers для регистрации политик на уровне конфигурации.
|
||||
|
||||
@@ -82,7 +82,7 @@
|
||||
- `Bot.UsePolicy(...)` и `Plugin.UsePolicy(...)` для удобной регистрации.
|
||||
- Встроенные Telegram-aware helpers: `RequirePrivateChat(...)`, `RequireGroupChat(...)`, `RequireSupergroupChat(...)`, `RequireChatAdmin(...)`, `RequireChatCreator(...)`, `RequireBotAdmin(...)` и `RequireCallbackFromUser(...)`.
|
||||
- Комбинаторы `AllPolicies(...)`, `AnyPolicy(...)` и `NotPolicy(...)`.
|
||||
- Расширенная нормализация `MsgContext` для `Chat` и `ChatID`, а также регрессионные тесты на поведение политик и нормализованного контекста.
|
||||
- Расширенная нормализация `MessageContext` для `Chat` и `ChatID`, а также регрессионные тесты на поведение политик и нормализованного контекста.
|
||||
|
||||
Практическая цель:
|
||||
|
||||
@@ -106,7 +106,7 @@
|
||||
|
||||
- `AsUserError(...)` и `AsInternalError(...)` для явной классификации возвращаемых ошибок.
|
||||
- `IsUserError(...)` и `IsInternalError(...)` для проверки этой классификации на стороне фреймворка.
|
||||
- Обновлённое поведение `MsgContext.Error(...)`: все ошибки по-прежнему логируются, но для внутренних ошибок автоматический ответ пользователю подавляется.
|
||||
- Обновлённое поведение `MessageContext.Error(...)`: все ошибки по-прежнему логируются, но для внутренних ошибок автоматический ответ пользователю подавляется.
|
||||
- Регрессионные тесты для message и callback потоков.
|
||||
|
||||
Практическая цель:
|
||||
@@ -117,7 +117,7 @@
|
||||
|
||||
Текущее состояние:
|
||||
|
||||
- Фреймворк теперь считает конфигурацию бота структурно завершённой после начала первого runtime entry point: `Run()`, `RunWithContext(...)` или `RunWebHookWithContext(...)`.
|
||||
- Фреймворк теперь считает конфигурацию бота структурно завершённой после начала первого runtime entry point: `Run()`, `RunWithContext(...)` или `RunWebhookWithContext(...)`.
|
||||
- Поздние bot-level попытки мутации больше не применяются частично после старта runtime.
|
||||
- Границы между регистрацией плагинов, стартом runtime и фиксацией конфигурации теперь оформлены как явное поведение фреймворка и закреплены тестами.
|
||||
|
||||
@@ -141,30 +141,30 @@
|
||||
Текущее состояние:
|
||||
|
||||
- Нормализация обновлений уже существовала, но теперь она описана и протестирована как явный контракт уровня фреймворка.
|
||||
- Категории маршрутизации и гарантии заполнения `MsgContext` теперь рассматриваются как полноценная часть публичной модели.
|
||||
- Категории маршрутизации и гарантии заполнения `MessageContext` теперь рассматриваются как полноценная часть публичной модели.
|
||||
|
||||
Почему это важно:
|
||||
|
||||
- Код обработчиков должен понимать, на какие поля `MsgContext` можно безопасно опираться в каждом потоке обновлений.
|
||||
- Код обработчиков должен понимать, на какие поля `MessageContext` можно безопасно опираться в каждом потоке обновлений.
|
||||
- Без явного контракта обработка обновлений остаётся понятной только через чтение реализации.
|
||||
|
||||
Что теперь есть:
|
||||
|
||||
- Задокументированная модель маршрутизации для command flow, payload flow и generic update handlers.
|
||||
- Явные комментарии на полях `MsgContext`, описывающие гарантии для update-backed, callback-backed и message-backed контекстов.
|
||||
- Явные комментарии на полях `MessageContext`, описывающие гарантии для update-backed, callback-backed и message-backed контекстов.
|
||||
- Table-driven регрессионные тесты для нормализованного update contract, включая callback target semantics и non-command update flows.
|
||||
|
||||
Практическая цель:
|
||||
|
||||
- Сделать маршрутизацию обновлений и гарантии `MsgContext` достаточно явными, чтобы на них можно было опираться как на стабильный контракт `1.0`.
|
||||
- Сделать маршрутизацию обновлений и гарантии `MessageContext` достаточно явными, чтобы на них можно было опираться как на стабильный контракт `1.0`.
|
||||
|
||||
### [1.0.0-rc.12] Conversation / Scene Model
|
||||
|
||||
Текущее состояние:
|
||||
|
||||
- Фреймворк хорошо обрабатывает одно обновление через команды, данные callback, middleware и обработчики обновлений.
|
||||
- В нём уже есть полезные низкоуровневые строительные блоки: `MsgContext`, черновики, маршрутизация данных callback, плагины и обработчики обновлений.
|
||||
- Теперь в нём уже есть реализованная начальная модель сцен для долгоживущих интерактивных сценариев: сцены можно регистрировать в плагинах, запускать через `MsgContext`, сохранять через `SessionStore` и маршрутизировать раньше обычной обработки команд.
|
||||
- В нём уже есть полезные низкоуровневые строительные блоки: `MessageContext`, черновики, маршрутизация данных callback, плагины и обработчики обновлений.
|
||||
- Теперь в нём уже есть реализованная начальная модель сцен для долгоживущих интерактивных сценариев: сцены можно регистрировать в плагинах, запускать через `MessageContext`, сохранять через `SessionStore` и маршрутизировать раньше обычной обработки команд.
|
||||
|
||||
Почему это важно:
|
||||
|
||||
@@ -176,7 +176,7 @@
|
||||
|
||||
- Маршрутизация активной сцены раньше обычной маршрутизации команд.
|
||||
- Области действия сессии на пользователя, чат и пару пользователь-чат.
|
||||
- Явный вход и выход через `MsgContext`.
|
||||
- Явный вход и выход через `MessageContext`.
|
||||
- Обработчики шагов, локальные команды сцены и `OnMessage(...)`.
|
||||
- Встроенное in-memory-хранилище по умолчанию и интерфейс `SessionStore` для собственного постоянного хранения.
|
||||
|
||||
@@ -190,7 +190,7 @@
|
||||
|
||||
- `Scene`, `SceneContext`, `SceneSession` и `SessionStore`.
|
||||
- `Plugin.NewScene(...)` и `Plugin.AddScene(...)`.
|
||||
- `MsgContext.EnterScene(...)`, `EnterSceneStep(...)` и `ExitScene(...)`.
|
||||
- `MessageContext.EnterScene(...)`, `EnterSceneStep(...)` и `ExitScene(...)`.
|
||||
- `SceneContext.Stay()`, `Next(...)`, `Exit()`, `Pass()`, `BindData(...)` и `SaveData(...)`.
|
||||
- Состояние на пользователя или чат с хранением в `SessionStore` и чистым интерфейсом для собственного постоянного хранения.
|
||||
|
||||
@@ -198,7 +198,7 @@
|
||||
|
||||
- Это должно быть опциональным и расширяющим текущую модель.
|
||||
- Это не должно заменять плагины, команды или обработчики как обычные точки входа во фреймворк.
|
||||
- Это должно работать поверх существующих middleware и `MsgContext`, а не вводить вторую несовместимую модель выполнения.
|
||||
- Это должно работать поверх существующих middleware и `MessageContext`, а не вводить вторую несовместимую модель выполнения.
|
||||
|
||||
Практическая цель:
|
||||
|
||||
@@ -240,7 +240,7 @@ type BanInput struct {
|
||||
Reason string
|
||||
}
|
||||
|
||||
func ban(ctx *laniakea.MsgContext, db *App) error {
|
||||
func ban(ctx *laniakea.MessageContext, db *App) error {
|
||||
var input BanInput
|
||||
if err := ctx.BindArgs(&input); err != nil {
|
||||
return err
|
||||
@@ -263,7 +263,7 @@ func ban(ctx *laniakea.MsgContext, db *App) error {
|
||||
|
||||
Текущее состояние:
|
||||
|
||||
- `RunWithContext(...)` и `RunWebHookWithContext(...)` управляют жизненным циклом выполнения бота и корректным завершением.
|
||||
- `RunWithContext(...)` и `RunWebhookWithContext(...)` управляют жизненным циклом выполнения бота и корректным завершением.
|
||||
- `tgapi` уже поддерживает методы, принимающие `context.Context`.
|
||||
- Обычные обработчики не получают полноценный `context.Context`, привязанный к обработке конкретного запроса.
|
||||
|
||||
@@ -281,14 +281,14 @@ func ban(ctx *laniakea.MsgContext, db *App) error {
|
||||
|
||||
Возможное направление API:
|
||||
|
||||
- Предпочесть non-breaking подход и выдавать context через `MsgContext`, например `ctx.Context()`.
|
||||
- Предпочесть non-breaking подход и выдавать context через `MessageContext`, например `ctx.Context()`.
|
||||
- Строить context из жизненного цикла обработки обновления, чтобы он оставался полезным во время корректного завершения.
|
||||
- Сделать естественной передачу этого context в методы базы данных, HTTP-клиенты и `tgapi.WithContext(...)`.
|
||||
|
||||
Почему это, скорее всего, не должно быть изменением сигнатуры:
|
||||
|
||||
- Изменение сигнатур обработчиков на прямой `context.Context` было бы публичным ломающим изменением.
|
||||
- Аксессор на `MsgContext` сохраняет совместимость и при этом даёт обработчикам идиоматичный Go-путь для отмены выполнения.
|
||||
- Аксессор на `MessageContext` сохраняет совместимость и при этом даёт обработчикам идиоматичный Go-путь для отмены выполнения.
|
||||
|
||||
Практическая цель:
|
||||
|
||||
@@ -297,7 +297,7 @@ func ban(ctx *laniakea.MsgContext, db *App) error {
|
||||
Связанные страницы:
|
||||
|
||||
- [[Scenes]]
|
||||
- [[MsgContext]]
|
||||
- [[MessageContext]]
|
||||
- [[Bot-Lifecycle]]
|
||||
- [[Migration]]
|
||||
|
||||
|
||||
+22
-22
@@ -9,7 +9,7 @@ This page tracks framework-level backlog items that are about missing concepts i
|
||||
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 +21,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:
|
||||
@@ -65,7 +65,7 @@ Practical target:
|
||||
|
||||
Current state:
|
||||
|
||||
- The framework now exposes `Policy[T]` as a first-class reusable authorization rule that runs against the normalized `MsgContext` and injected app data.
|
||||
- The framework now exposes `Policy[T]` as a first-class reusable authorization rule that runs against the normalized `MessageContext` and injected app data.
|
||||
- Policies integrate with the existing execution model through `RequirePolicy(...)`, so authorization stays on the middleware path instead of introducing a second routing pipeline.
|
||||
- Bot-level and plugin-level registration helpers now make policy usage explicit in configuration.
|
||||
|
||||
@@ -82,7 +82,7 @@ What is now present:
|
||||
- `Bot.UsePolicy(...)` and `Plugin.UsePolicy(...)` for registration ergonomics.
|
||||
- Built-in Telegram-aware helpers such as `RequirePrivateChat(...)`, `RequireGroupChat(...)`, `RequireSupergroupChat(...)`, `RequireChatAdmin(...)`, `RequireChatCreator(...)`, `RequireBotAdmin(...)`, and `RequireCallbackFromUser(...)`.
|
||||
- Composition helpers `AllPolicies(...)`, `AnyPolicy(...)`, and `NotPolicy(...)`.
|
||||
- Extended `MsgContext` normalization for `Chat` and `ChatID`, plus regression coverage for policy and normalization behavior.
|
||||
- Extended `MessageContext` normalization for `Chat` and `ChatID`, plus regression coverage for policy and normalization behavior.
|
||||
|
||||
Practical target:
|
||||
|
||||
@@ -106,7 +106,7 @@ What is now present:
|
||||
|
||||
- `AsUserError(...)` and `AsInternalError(...)` to classify returned handler errors explicitly.
|
||||
- `IsUserError(...)` and `IsInternalError(...)` for framework-side inspection.
|
||||
- Updated centralized `MsgContext.Error(...)` behavior that still logs all errors but suppresses the automatic user reply for internal-only failures.
|
||||
- Updated centralized `MessageContext.Error(...)` behavior that still logs all errors but suppresses the automatic user reply for internal-only failures.
|
||||
- Regression coverage for both message and callback flows.
|
||||
|
||||
Practical target:
|
||||
@@ -117,7 +117,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.
|
||||
|
||||
@@ -141,30 +141,30 @@ Practical target:
|
||||
Current state:
|
||||
|
||||
- Update normalization already existed, but it is now described and tested as an explicit framework-level contract.
|
||||
- Routing categories and `MsgContext` population guarantees are now treated as a first-class part of the public model.
|
||||
- Routing categories and `MessageContext` population guarantees are now treated as a first-class part of the public model.
|
||||
|
||||
Why this matters:
|
||||
|
||||
- Handler code needs to know which `MsgContext` fields are safe to rely on for each update path.
|
||||
- Handler code needs to know which `MessageContext` fields are safe to rely on for each update path.
|
||||
- Without a formal contract, update handling remains understandable only by reading implementation details.
|
||||
|
||||
What is now present:
|
||||
|
||||
- A documented routing model for command flow, payload flow, and generic update handlers.
|
||||
- Explicit `MsgContext` field comments for update-backed, callback-backed, and message-backed contexts.
|
||||
- Explicit `MessageContext` field comments for update-backed, callback-backed, and message-backed contexts.
|
||||
- Table-driven regression coverage for the normalized update contract, including callback target semantics and non-command update flows.
|
||||
|
||||
Practical target:
|
||||
|
||||
- Make update routing and `MsgContext` guarantees explicit enough to serve as a stable `1.0` public contract.
|
||||
- Make update routing and `MessageContext` guarantees explicit enough to serve as a stable `1.0` public contract.
|
||||
|
||||
### [1.0.0-rc.12] Conversation / Scene Model
|
||||
|
||||
Current state:
|
||||
|
||||
- The framework is strong at handling a single update through commands, payloads, middleware, and update handlers.
|
||||
- It already has useful lower-level building blocks such as `MsgContext`, drafts, payload routing, plugins, and update handlers.
|
||||
- It now provides an implemented initial scene model for long-lived user interaction flows: scenes can be registered in plugins, entered through `MsgContext`, persisted through `SessionStore`, and routed before normal command handling.
|
||||
- It already has useful lower-level building blocks such as `MessageContext`, drafts, payload routing, plugins, and update handlers.
|
||||
- It now provides an implemented initial scene model for long-lived user interaction flows: scenes can be registered in plugins, entered through `MessageContext`, persisted through `SessionStore`, and routed before normal command handling.
|
||||
|
||||
Why this matters:
|
||||
|
||||
@@ -176,7 +176,7 @@ What is already present:
|
||||
|
||||
- Active-scene routing before normal command flow.
|
||||
- Per-user, per-chat, and per-user-chat session scopes.
|
||||
- Explicit scene entry and exit through `MsgContext`.
|
||||
- Explicit scene entry and exit through `MessageContext`.
|
||||
- Step handlers, scene-local commands, and `OnMessage(...)`.
|
||||
- In-memory session storage by default, plus the `SessionStore` interface for custom persistence.
|
||||
|
||||
@@ -190,7 +190,7 @@ Current API direction:
|
||||
|
||||
- `Scene`, `SceneContext`, `SceneSession`, and `SessionStore`.
|
||||
- `Plugin.NewScene(...)` and `Plugin.AddScene(...)`.
|
||||
- `MsgContext.EnterScene(...)`, `EnterSceneStep(...)`, and `ExitScene(...)`.
|
||||
- `MessageContext.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.
|
||||
|
||||
@@ -198,7 +198,7 @@ Important design constraints:
|
||||
|
||||
- This should be additive and optional.
|
||||
- It should not replace plugins, commands, or handlers as the normal framework entry points.
|
||||
- It should work with existing middleware and `MsgContext` instead of introducing a second incompatible execution model.
|
||||
- It should work with existing middleware and `MessageContext` instead of introducing a second incompatible execution model.
|
||||
|
||||
Practical target:
|
||||
|
||||
@@ -240,7 +240,7 @@ type BanInput struct {
|
||||
Reason string
|
||||
}
|
||||
|
||||
func ban(ctx *laniakea.MsgContext, db *App) error {
|
||||
func ban(ctx *laniakea.MessageContext, db *App) error {
|
||||
var input BanInput
|
||||
if err := ctx.BindArgs(&input); err != nil {
|
||||
return err
|
||||
@@ -263,7 +263,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`.
|
||||
|
||||
@@ -281,14 +281,14 @@ What is missing:
|
||||
|
||||
Possible API direction:
|
||||
|
||||
- Prefer a non-breaking approach by exposing context through `MsgContext`, for example `ctx.Context()`.
|
||||
- Prefer a non-breaking approach by exposing context through `MessageContext`, for example `ctx.Context()`.
|
||||
- Build the context from the update-processing lifecycle so it is meaningful during graceful shutdown.
|
||||
- Make it natural to pass that context into database methods, HTTP clients, and `tgapi.WithContext(...)` calls.
|
||||
|
||||
Why this should probably not be a signature change:
|
||||
|
||||
- Changing handler signatures to accept `context.Context` directly would be a public breaking change.
|
||||
- A `MsgContext` accessor would preserve compatibility while still giving handlers an idiomatic Go cancellation path.
|
||||
- A `MessageContext` accessor would preserve compatibility while still giving handlers an idiomatic Go cancellation path.
|
||||
|
||||
Practical target:
|
||||
|
||||
@@ -297,7 +297,7 @@ Practical target:
|
||||
Related pages:
|
||||
|
||||
- [[Scenes]]
|
||||
- [[MsgContext]]
|
||||
- [[MessageContext]]
|
||||
- [[Bot-Lifecycle]]
|
||||
- [[Migration]]
|
||||
|
||||
|
||||
+7
-7
@@ -33,7 +33,7 @@ import (
|
||||
"git.scuroneko.dev/scuroneko/laniakea"
|
||||
)
|
||||
|
||||
func ping(ctx *laniakea.MsgContext, db laniakea.NoData) error {
|
||||
func ping(ctx *laniakea.MessageContext, db laniakea.NoData) error {
|
||||
ctx.Answer("Pong")
|
||||
return nil
|
||||
}
|
||||
@@ -111,7 +111,7 @@ bot.AddPlugins(plugin)
|
||||
Сигнатура хендлера:
|
||||
|
||||
```go
|
||||
func(ctx *laniakea.MsgContext, db T) error
|
||||
func(ctx *laniakea.MessageContext, db T) error
|
||||
```
|
||||
|
||||
То есть:
|
||||
@@ -121,7 +121,7 @@ func(ctx *laniakea.MsgContext, db T) error
|
||||
Пример:
|
||||
|
||||
```go
|
||||
func profile(ctx *laniakea.MsgContext, db *App) error {
|
||||
func profile(ctx *laniakea.MessageContext, db *App) error {
|
||||
user, err := db.LoadUser(ctx.FromID)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -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()`
|
||||
|
||||
## Частые ошибки на старте
|
||||
@@ -199,4 +199,4 @@ defer bot.Close()
|
||||
- [[Bot-Lifecycle-RU]]
|
||||
- [[Getting-Started]]
|
||||
- [[Commands-and-Plugins]]
|
||||
- [[MsgContext]]
|
||||
- [[MessageContext]]
|
||||
|
||||
+8
-8
@@ -34,7 +34,7 @@ import (
|
||||
"git.scuroneko.dev/scuroneko/laniakea"
|
||||
)
|
||||
|
||||
func ping(ctx *laniakea.MsgContext, db laniakea.NoData) error {
|
||||
func ping(ctx *laniakea.MessageContext, db laniakea.NoData) error {
|
||||
ctx.Answer("Pong")
|
||||
return nil
|
||||
}
|
||||
@@ -113,7 +113,7 @@ See [[Commands-and-Plugins]] for the full model.
|
||||
The command handler signature is:
|
||||
|
||||
```go
|
||||
func(ctx *laniakea.MsgContext, db T) error
|
||||
func(ctx *laniakea.MessageContext, db T) error
|
||||
```
|
||||
|
||||
This means:
|
||||
@@ -124,7 +124,7 @@ This means:
|
||||
Example:
|
||||
|
||||
```go
|
||||
func profile(ctx *laniakea.MsgContext, db *App) error {
|
||||
func profile(ctx *laniakea.MessageContext, db *App) error {
|
||||
user, err := db.LoadUser(ctx.FromID)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -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
|
||||
@@ -184,7 +184,7 @@ import (
|
||||
|
||||
type App struct{}
|
||||
|
||||
func echo(ctx *laniakea.MsgContext, app *App) error {
|
||||
func echo(ctx *laniakea.MessageContext, app *App) error {
|
||||
if ctx.Text == "" {
|
||||
ctx.Answer("Send some text after the command.")
|
||||
return nil
|
||||
@@ -259,5 +259,5 @@ Prefer pointer types unless you have a strong reason not to.
|
||||
|
||||
## Where to go next
|
||||
- Read [[Commands-and-Plugins]] next if you want to build the handler layer correctly.
|
||||
- Read [[MsgContext]] next if you want to understand reply, edit, callback, and draft helpers.
|
||||
- Read [[MessageContext]] next if you want to understand reply, edit, callback, and draft helpers.
|
||||
- Read [[Bot-Lifecycle]] if you need shutdown, worker, or startup details.
|
||||
|
||||
+3
-3
@@ -9,7 +9,7 @@ English version: [[Home]]
|
||||
- [[Getting-Started-RU]]
|
||||
- [[Bot-Options-and-Configuration-RU]]
|
||||
- [[Commands-and-Plugins-RU]]
|
||||
- [[MsgContext-RU]]
|
||||
- [[MessageContext-RU]]
|
||||
|
||||
## Выполнение и архитектура
|
||||
|
||||
@@ -50,7 +50,7 @@ English version: [[Home]]
|
||||
- [[Getting-Started]]
|
||||
- [[Bot-Options-and-Configuration]]
|
||||
- [[Commands-and-Plugins]]
|
||||
- [[MsgContext]]
|
||||
- [[MessageContext]]
|
||||
- [[Bot-Lifecycle]]
|
||||
- [[Inline-Keyboards-and-Payloads]]
|
||||
- [[tgapi-Overview]]
|
||||
@@ -60,7 +60,7 @@ English version: [[Home]]
|
||||
Рекомендуемый маршрут для русскоязычного пользователя:
|
||||
|
||||
1. Прочитать [[Getting-Started-RU]].
|
||||
2. Прочитать [[Commands-and-Plugins-RU]] и [[MsgContext-RU]].
|
||||
2. Прочитать [[Commands-and-Plugins-RU]] и [[MessageContext-RU]].
|
||||
3. Перейти в страницы про выполнение и поведение во время работы, например [[Bot-Lifecycle-RU]] и [[Middleware-RU]].
|
||||
4. При необходимости открыть специализированные страницы вроде [[Drafts-RU]], [[Rate-Limiting-RU]] или [[tgapi-Overview-RU]].
|
||||
5. Для максимальной точности переходить в соответствующую англоязычную страницу.
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ Use this wiki as the structured companion to the README: start with setup, then
|
||||
- [[Getting-Started]]
|
||||
- [[Bot-Options-and-Configuration]]
|
||||
- [[Commands-and-Plugins]]
|
||||
- [[MsgContext]]
|
||||
- [[MessageContext]]
|
||||
|
||||
## Bot Runtime
|
||||
- [[Bot-Lifecycle]]
|
||||
|
||||
@@ -99,5 +99,5 @@ kb := ctx.NewInlineKeyboard(2).
|
||||
## Что читать дальше
|
||||
|
||||
- [[Commands-and-Plugins-RU]]
|
||||
- [[MsgContext-RU]]
|
||||
- [[MessageContext-RU]]
|
||||
- [[Inline-Keyboards-and-Payloads]]
|
||||
|
||||
@@ -91,7 +91,7 @@ Inside the payload handler, those values are available through:
|
||||
- `ctx.Args` for the decoded argument list;
|
||||
- the payload command name used to choose the matched handler.
|
||||
|
||||
There is no separate `ctx.Payload` object in the current API. Payload handlers receive the same `MsgContext` structure used elsewhere, with `ctx.Args` populated from callback data.
|
||||
There is no separate `ctx.Payload` object in the current API. Payload handlers receive the same `MessageContext` structure used elsewhere, with `ctx.Args` populated from callback data.
|
||||
|
||||
## JSON vs Base64 payloads
|
||||
|
||||
@@ -126,7 +126,7 @@ The bot has a default callback encoding:
|
||||
bot.SetPayloadType(laniakea.BotPayloadBase64)
|
||||
```
|
||||
|
||||
That default is copied into `MsgContext`, so `ctx.NewInlineKeyboard(...)` starts with the bot’s current payload type.
|
||||
That default is copied into `MessageContext`, so `ctx.NewInlineKeyboard(...)` starts with the bot’s current payload type.
|
||||
|
||||
For a single keyboard, you can override it locally:
|
||||
|
||||
@@ -247,7 +247,7 @@ Payloads are registered on plugins with:
|
||||
Example:
|
||||
|
||||
```go
|
||||
plugin.NewPayload(func(ctx *laniakea.MsgContext, db *App) error {
|
||||
plugin.NewPayload(func(ctx *laniakea.MessageContext, db *App) error {
|
||||
id := ctx.Args[0]
|
||||
ctx.AnswerCbQueryText("Handled " + id)
|
||||
return nil
|
||||
@@ -275,5 +275,5 @@ Related page:
|
||||
## Related pages
|
||||
|
||||
- [[Commands-and-Plugins]] for payload handler registration and routing.
|
||||
- [[MsgContext]] for reply, edit, and callback helpers.
|
||||
- [[MessageContext]] for reply, edit, and callback helpers.
|
||||
- [[tgapi-Overview]] for lower-level Telegram method access when keyboard helpers are not enough.
|
||||
|
||||
+5
-5
@@ -77,11 +77,11 @@ bot.SetL10n(l10n)
|
||||
|
||||
From then on:
|
||||
- `bot.L10n(lang, key)` is available for manual lookups;
|
||||
- `MsgContext.Translate(key)` becomes the ergonomic handler-level helper.
|
||||
- `MessageContext.Translate(key)` becomes the ergonomic handler-level helper.
|
||||
|
||||
If `SetL10n(nil)` is called, the bot logs a warning and keeps the existing localization provider unchanged.
|
||||
|
||||
## `MsgContext.Translate`
|
||||
## `MessageContext.Translate`
|
||||
|
||||
`ctx.Translate(key)` is the usual choice inside handlers.
|
||||
|
||||
@@ -93,7 +93,7 @@ It:
|
||||
Example:
|
||||
|
||||
```go
|
||||
func start(ctx *laniakea.MsgContext, db *App) error {
|
||||
func start(ctx *laniakea.MessageContext, db *App) error {
|
||||
ctx.Answer(ctx.Translate("greeting"))
|
||||
return nil
|
||||
}
|
||||
@@ -111,7 +111,7 @@ Use `bot.L10n(lang, key)` when you need a translation outside handler flow or wh
|
||||
Examples:
|
||||
- preparing background-runner messages;
|
||||
- rendering text for stored user preferences;
|
||||
- translating outside `MsgContext`.
|
||||
- translating outside `MessageContext`.
|
||||
|
||||
## Concurrency and mutation safety
|
||||
|
||||
@@ -150,5 +150,5 @@ l10n.
|
||||
## Related pages
|
||||
|
||||
- [[Getting-Started]] for basic bot setup
|
||||
- [[MsgContext]] for handler helpers
|
||||
- [[MessageContext]] for handler helpers
|
||||
- [[Recipes]] for localized handler examples
|
||||
|
||||
+2
-2
@@ -55,13 +55,13 @@ Ways plugin loggers are set:
|
||||
- explicitly with `Plugin.SetLogger(...)` before `AddPlugins(...)`;
|
||||
- implicitly by the bot, which creates a default logger if the plugin has none at registration time.
|
||||
|
||||
During handler execution, `MsgContext.Logger` is set to:
|
||||
During handler execution, `MessageContext.Logger` is set to:
|
||||
- the matched plugin logger when one exists;
|
||||
- otherwise the bot logger.
|
||||
|
||||
That means handler-local logs naturally follow plugin boundaries when possible.
|
||||
|
||||
## `MsgContext.Logger`
|
||||
## `MessageContext.Logger`
|
||||
|
||||
Inside handlers and middleware, the easiest logger to use is `ctx.Logger`.
|
||||
|
||||
|
||||
+4
-4
@@ -45,7 +45,7 @@ Middleware позволяет запускать логику до обрабо
|
||||
Сигнатура:
|
||||
|
||||
```go
|
||||
func(ctx *laniakea.MsgContext, db T) bool
|
||||
func(ctx *laniakea.MessageContext, db T) bool
|
||||
```
|
||||
|
||||
Возвращает:
|
||||
@@ -66,7 +66,7 @@ func(ctx *laniakea.MsgContext, db T) bool
|
||||
- middleware идет в goroutine;
|
||||
- выполнение цепочки продолжается сразу;
|
||||
- возвращаемое значение `bool` игнорируется;
|
||||
- middleware получает копию `MsgContext`.
|
||||
- middleware получает копию `MessageContext`.
|
||||
|
||||
Поэтому async middleware подходит только для:
|
||||
- телеметрии;
|
||||
@@ -79,7 +79,7 @@ func(ctx *laniakea.MsgContext, db T) bool
|
||||
- обязательной валидации;
|
||||
- логики, которая должна переписать `ctx` и повлиять на обработчик.
|
||||
|
||||
## Почему async middleware получает копию `MsgContext`
|
||||
## Почему async middleware получает копию `MessageContext`
|
||||
|
||||
Так библиотека избегает очевидных гонок данных между goroutine middleware и основной цепочкой обработки.
|
||||
|
||||
@@ -105,5 +105,5 @@ Middleware уровня плагина и команды сохраняют по
|
||||
## Что читать дальше
|
||||
|
||||
- [[Commands-and-Plugins-RU]]
|
||||
- [[MsgContext-RU]]
|
||||
- [[MessageContext-RU]]
|
||||
- [[Middleware]]
|
||||
|
||||
+12
-12
@@ -11,11 +11,11 @@ Middleware is useful when the same check or side effect should apply in more tha
|
||||
Typical uses:
|
||||
- reject updates from unauthorized users;
|
||||
- log incoming commands and callback payloads;
|
||||
- attach derived values to `MsgContext`;
|
||||
- attach derived values to `MessageContext`;
|
||||
- stop processing early when a precondition is not met;
|
||||
- run non-blocking side effects such as analytics or audit logging.
|
||||
|
||||
For the handler and plugin model around middleware, see [[Commands-and-Plugins]]. For the fields you can read or update on the context, see [[MsgContext]].
|
||||
For the handler and plugin model around middleware, see [[Commands-and-Plugins]]. For the fields you can read or update on the context, see [[MessageContext]].
|
||||
|
||||
## Middleware levels
|
||||
|
||||
@@ -47,7 +47,7 @@ For non-command update handlers registered through `AddUpdateHandler(...)`, the
|
||||
|
||||
Important detail:
|
||||
- bot-level middleware runs once per update before routing;
|
||||
- for non-command update handlers, each matching plugin receives its own cloned `MsgContext`, so one plugin's mutations do not leak into the next plugin's handler chain.
|
||||
- for non-command update handlers, each matching plugin receives its own cloned `MessageContext`, so one plugin's mutations do not leak into the next plugin's handler chain.
|
||||
|
||||
## Synchronous middleware
|
||||
|
||||
@@ -56,7 +56,7 @@ By default, middleware is synchronous.
|
||||
The executor signature is:
|
||||
|
||||
```go
|
||||
func(ctx *laniakea.MsgContext, db T) bool
|
||||
func(ctx *laniakea.MessageContext, db T) bool
|
||||
```
|
||||
|
||||
Return values mean:
|
||||
@@ -72,7 +72,7 @@ This makes synchronous middleware the right choice for:
|
||||
Example:
|
||||
|
||||
```go
|
||||
auth := laniakea.NewMiddleware("auth", func(ctx *laniakea.MsgContext, db *App) bool {
|
||||
auth := laniakea.NewMiddleware("auth", func(ctx *laniakea.MessageContext, db *App) bool {
|
||||
if ctx.From == nil || !db.Allowed(ctx.From.ID) {
|
||||
ctx.Answer("Access denied")
|
||||
return false
|
||||
@@ -86,7 +86,7 @@ auth := laniakea.NewMiddleware("auth", func(ctx *laniakea.MsgContext, db *App) b
|
||||
Middleware can also run asynchronously with `SetAsync(true)`.
|
||||
|
||||
```go
|
||||
audit := laniakea.NewMiddleware("audit", func(ctx *laniakea.MsgContext, db *App) bool {
|
||||
audit := laniakea.NewMiddleware("audit", func(ctx *laniakea.MessageContext, db *App) bool {
|
||||
db.Audit(ctx.Update.UpdateID, ctx.Text)
|
||||
return true
|
||||
}).SetAsync(true)
|
||||
@@ -96,7 +96,7 @@ Async middleware behaves differently:
|
||||
- it runs in a goroutine;
|
||||
- execution always continues immediately;
|
||||
- its boolean return value is ignored;
|
||||
- it receives a copied `MsgContext`, not the original pointer.
|
||||
- it receives a copied `MessageContext`, not the original pointer.
|
||||
|
||||
That means async middleware is appropriate for:
|
||||
- fire-and-forget logging;
|
||||
@@ -112,7 +112,7 @@ It is not appropriate for:
|
||||
|
||||
## Why async middleware gets a copied context
|
||||
|
||||
When middleware is async, the library copies `MsgContext` before starting the goroutine. This prevents obvious data races against the handler path.
|
||||
When middleware is async, the library copies `MessageContext` before starting the goroutine. This prevents obvious data races against the handler path.
|
||||
|
||||
Practical consequence:
|
||||
- changes you make to the copied `ctx` inside async middleware are local to that goroutine;
|
||||
@@ -121,7 +121,7 @@ Practical consequence:
|
||||
So this pattern does not work:
|
||||
|
||||
```go
|
||||
bad := laniakea.NewMiddleware("bad", func(ctx *laniakea.MsgContext, db *App) bool {
|
||||
bad := laniakea.NewMiddleware("bad", func(ctx *laniakea.MessageContext, db *App) bool {
|
||||
ctx.Text = "rewritten"
|
||||
return false
|
||||
}).SetAsync(true)
|
||||
@@ -177,7 +177,7 @@ This usually means:
|
||||
|
||||
```go
|
||||
bot.AddMiddleware(
|
||||
laniakea.NewMiddleware("private-only", func(ctx *laniakea.MsgContext, db *App) bool {
|
||||
laniakea.NewMiddleware("private-only", func(ctx *laniakea.MessageContext, db *App) bool {
|
||||
if ctx.Chat == nil || ctx.Chat.Type != "private" {
|
||||
return false
|
||||
}
|
||||
@@ -191,7 +191,7 @@ bot.AddMiddleware(
|
||||
```go
|
||||
admin := laniakea.NewPlugin[*App]("admin")
|
||||
admin.AddMiddleware(
|
||||
laniakea.NewMiddleware("admin-only", func(ctx *laniakea.MsgContext, db *App) bool {
|
||||
laniakea.NewMiddleware("admin-only", func(ctx *laniakea.MessageContext, db *App) bool {
|
||||
return ctx.From != nil && db.IsAdmin(ctx.From.ID)
|
||||
}),
|
||||
)
|
||||
@@ -201,7 +201,7 @@ admin.AddMiddleware(
|
||||
|
||||
```go
|
||||
ban := admin.NewCommand(banUser, "ban")
|
||||
ban.Use(laniakea.NewMiddleware("require-reply", func(ctx *laniakea.MsgContext, db *App) bool {
|
||||
ban.Use(laniakea.NewMiddleware("require-reply", func(ctx *laniakea.MessageContext, db *App) bool {
|
||||
if ctx.Msg == nil || ctx.Msg.ReplyToMessage == nil {
|
||||
ctx.Answer("Reply to a user message first")
|
||||
return false
|
||||
|
||||
+64
-1
@@ -25,7 +25,70 @@ When jumping across multiple RC versions:
|
||||
5. Revisit any direct `tgapi` calls and renamed types.
|
||||
6. Run tests against realistic update payloads and callback data.
|
||||
|
||||
The largest migration points in the current history are `rc.4`, `rc.7`, `rc.10`, and `rc.12`.
|
||||
The largest migration points in the current history are `rc.4`, `rc.7`, `rc.10`, `rc.12`, and the `v1.0.0` stable release.
|
||||
|
||||
## `v1.0.0`
|
||||
|
||||
`v1.0.0` is a public-API hygiene release. Most changes are renames and type-system tightenings that cause compile errors and are easy to fix mechanically.
|
||||
|
||||
### Handler type rename: `MsgContext` → `MessageContext`
|
||||
|
||||
Every handler signature and every explicit type reference to `MsgContext` must be renamed to `MessageContext`.
|
||||
|
||||
```go
|
||||
// before
|
||||
func ping(ctx *laniakea.MsgContext, db *App) error { ... }
|
||||
|
||||
// after
|
||||
func ping(ctx *laniakea.MessageContext, db *App) error { ... }
|
||||
```
|
||||
|
||||
This includes `CommandExecutor`, `MiddlewareExecutor`, `SceneContext.MessageContext`, and any local variable type annotations.
|
||||
|
||||
### Runner builder change: `Onetime` and `Timeout` removed
|
||||
|
||||
The `Onetime(bool)` and `Timeout(duration)` builder methods have been replaced by `Every(duration)` and `Async(bool)`.
|
||||
|
||||
```go
|
||||
// before — one-time sync
|
||||
runner.Onetime(true).Async(false)
|
||||
|
||||
// after — one-time sync
|
||||
runner.Async(false)
|
||||
|
||||
// before — periodic
|
||||
runner.Timeout(5 * time.Minute)
|
||||
|
||||
// after — periodic
|
||||
runner.Every(5 * time.Minute)
|
||||
```
|
||||
|
||||
The default remains async one-shot (`Every(0).Async(true)`), so runners without a builder call are unaffected.
|
||||
|
||||
### Observer method renames
|
||||
|
||||
If you implement the `Observer` interface directly, rename the two affected methods:
|
||||
|
||||
| Before | After |
|
||||
|---|---|
|
||||
| `OnReceiveUpdate(UpdateReceivedEvent)` | `OnUpdateReceived(UpdateReceivedEvent)` |
|
||||
| `OnHandledUpdate(UpdateHandledEvent)` | `OnUpdateHandled(UpdateHandledEvent)` |
|
||||
|
||||
### `Scene.PluginName` unexported
|
||||
|
||||
`Scene.PluginName` was a mutable public field. It is now unexported. Remove any reads or writes to this field; the framework assigns it during `AddPlugins(...)` registration.
|
||||
|
||||
### `SceneSession.Data` unexported
|
||||
|
||||
`SceneSession.Data []byte` was a public field. It is now unexported. Use the accessor helpers: `HasData()`, `BindData(...)`, `SaveData(...)`, `ClearData()`.
|
||||
|
||||
### `BotPayloadType*` constants are now `const`
|
||||
|
||||
`BotPayloadTypeJSON`, `BotPayloadTypeBase64`, `BotPayloadTypeCompact`, and `BotPayloadTypeCompactBase64` were `var`. They are now `const`. Any code assigning to them will fail to compile.
|
||||
|
||||
### Webhook error sentinels
|
||||
|
||||
Inline `errors.New(...)` error values returned from webhook startup have been replaced by exported sentinels. If you were comparing webhook startup errors with `==`, switch to `errors.Is(...)`.
|
||||
|
||||
## `v1.0.0-rc.12`
|
||||
|
||||
|
||||
+9
-9
@@ -1,12 +1,12 @@
|
||||
# MsgContext RU
|
||||
# MessageContext RU
|
||||
|
||||
English version: [[MsgContext]]
|
||||
English version: [[MessageContext]]
|
||||
|
||||
Это краткая русскоязычная версия страницы про `MsgContext`. Полная и наиболее актуальная страница: [[MsgContext]].
|
||||
Это краткая русскоязычная версия страницы про `MessageContext`. Полная и наиболее актуальная страница: [[MessageContext]].
|
||||
|
||||
## Что такое `MsgContext`
|
||||
## Что такое `MessageContext`
|
||||
|
||||
`MsgContext` — это объект времени выполнения, который приходит в:
|
||||
`MessageContext` — это объект времени выполнения, который приходит в:
|
||||
- обработчики команд;
|
||||
- обработчики данных callback;
|
||||
- middleware;
|
||||
@@ -18,7 +18,7 @@ English version: [[MsgContext]]
|
||||
- разобранные аргументы команд и callback;
|
||||
- вспомогательные методы для reply, edit, delete, callback, drafts и localization.
|
||||
|
||||
Полную матрицу маршрутизации и гарантий по полям `MsgContext` для разных update types смотри в [[Update-Routing-Model-RU]].
|
||||
Полную матрицу маршрутизации и гарантий по полям `MessageContext` для разных update types смотри в [[Update-Routing-Model-RU]].
|
||||
|
||||
## Поля, которые используются чаще всего
|
||||
|
||||
@@ -107,12 +107,12 @@ English version: [[MsgContext]]
|
||||
|
||||
## Drafts и localization
|
||||
|
||||
У `MsgContext` есть:
|
||||
У `MessageContext` есть:
|
||||
- `NewDraft()`
|
||||
- `NewDraftMarkdown()`
|
||||
- `Translate(key)`
|
||||
|
||||
Это делает `MsgContext` основной удобной точкой доступа почти для всего кода обработчиков.
|
||||
Это делает `MessageContext` основной удобной точкой доступа почти для всего кода обработчиков.
|
||||
|
||||
## `NewInlineKeyboard(...)`
|
||||
|
||||
@@ -130,4 +130,4 @@ kb := ctx.NewInlineKeyboard(2)
|
||||
- [[Inline-Keyboards-and-Payloads-RU]]
|
||||
- [[Drafts-RU]]
|
||||
- [[Localization-RU]]
|
||||
- [[MsgContext]]
|
||||
- [[MessageContext]]
|
||||
|
||||
+16
-16
@@ -1,8 +1,8 @@
|
||||
# MsgContext
|
||||
# MessageContext
|
||||
|
||||
Russian version: [[MsgContext-RU]]
|
||||
Russian version: [[MessageContext-RU]]
|
||||
|
||||
`MsgContext` is the runtime object passed into command handlers, payload handlers, middleware, and update handlers.
|
||||
`MessageContext` is the runtime object passed into command handlers, payload handlers, middleware, and update handlers.
|
||||
|
||||
It gives you access to:
|
||||
- the incoming update
|
||||
@@ -10,7 +10,7 @@ It gives you access to:
|
||||
- parsed command or payload arguments
|
||||
- reply, edit, delete, callback, draft, and localization helpers
|
||||
|
||||
If you write handlers, `MsgContext` is the API surface you will use most often.
|
||||
If you write handlers, `MessageContext` is the API surface you will use most often.
|
||||
|
||||
For the full routing and field-guarantee matrix by update kind, see [[Update-Routing-Model]].
|
||||
|
||||
@@ -63,7 +63,7 @@ Use `FromID` when you only need the identifier and do not want to keep checking
|
||||
Use `Answer(...)` for the normal “reply with text” case.
|
||||
|
||||
```go
|
||||
func start(ctx *laniakea.MsgContext, db laniakea.NoData) error {
|
||||
func start(ctx *laniakea.MessageContext, db laniakea.NoData) error {
|
||||
ctx.Answer("Welcome")
|
||||
return nil
|
||||
}
|
||||
@@ -76,7 +76,7 @@ This is the default high-level reply helper for plain text.
|
||||
Use `AnswerLong(...)` when plain text may exceed Telegram’s message limit.
|
||||
|
||||
```go
|
||||
func help(ctx *laniakea.MsgContext, db laniakea.NoData) error {
|
||||
func help(ctx *laniakea.MessageContext, db laniakea.NoData) error {
|
||||
ctx.AnswerLong(buildLargeHelpText())
|
||||
return nil
|
||||
}
|
||||
@@ -92,7 +92,7 @@ Important:
|
||||
Use `Keyboard(...)` when you want to send a message with an inline keyboard.
|
||||
|
||||
```go
|
||||
func menu(ctx *laniakea.MsgContext, db laniakea.NoData) error {
|
||||
func menu(ctx *laniakea.MessageContext, db laniakea.NoData) error {
|
||||
kb := ctx.NewInlineKeyboard(2).
|
||||
AddCallbackButton("Profile", "profile.open").
|
||||
AddCallbackButton("Settings", "settings.open")
|
||||
@@ -127,7 +127,7 @@ Use `laniakea.EscapeMarkdownV2(...)` for this.
|
||||
Example:
|
||||
|
||||
```go
|
||||
func whoami(ctx *laniakea.MsgContext, db laniakea.NoData) error {
|
||||
func whoami(ctx *laniakea.MessageContext, db laniakea.NoData) error {
|
||||
name := laniakea.EscapeMarkdownV2(ctx.From.FirstName)
|
||||
ctx.AnswerMarkdown("*User:* " + name)
|
||||
return nil
|
||||
@@ -141,7 +141,7 @@ Once you already have an `AnswerMessage`, you can edit or delete it.
|
||||
Example:
|
||||
|
||||
```go
|
||||
func slowTask(ctx *laniakea.MsgContext, db laniakea.NoData) error {
|
||||
func slowTask(ctx *laniakea.MessageContext, db laniakea.NoData) error {
|
||||
msg := ctx.Answer("Working...")
|
||||
if msg == nil {
|
||||
return nil
|
||||
@@ -173,7 +173,7 @@ When handling inline button callbacks, these helpers are especially useful.
|
||||
Edits the callback-linked message.
|
||||
|
||||
```go
|
||||
func approve(ctx *laniakea.MsgContext, db laniakea.NoData) error {
|
||||
func approve(ctx *laniakea.MessageContext, db laniakea.NoData) error {
|
||||
ctx.EditCallback("Approved", nil)
|
||||
return nil
|
||||
}
|
||||
@@ -192,7 +192,7 @@ Use:
|
||||
Example:
|
||||
|
||||
```go
|
||||
func approve(ctx *laniakea.MsgContext, db laniakea.NoData) error {
|
||||
func approve(ctx *laniakea.MessageContext, db laniakea.NoData) error {
|
||||
ctx.AnswerCbQueryText("Saved")
|
||||
ctx.EditCallback("Saved", nil)
|
||||
return nil
|
||||
@@ -220,7 +220,7 @@ Caption rules differ from normal message text:
|
||||
|
||||
## Drafts
|
||||
|
||||
`MsgContext` also exposes draft creation helpers:
|
||||
`MessageContext` also exposes draft creation helpers:
|
||||
- `NewDraft()`
|
||||
- `NewDraftMarkdown()`
|
||||
|
||||
@@ -246,7 +246,7 @@ This looks up text using the current user’s language when available and falls
|
||||
Example:
|
||||
|
||||
```go
|
||||
func ping(ctx *laniakea.MsgContext, db laniakea.NoData) error {
|
||||
func ping(ctx *laniakea.MessageContext, db laniakea.NoData) error {
|
||||
ctx.Answer(ctx.Translate("ping.answer"))
|
||||
return nil
|
||||
}
|
||||
@@ -275,7 +275,7 @@ Use `SendAction(...)` to show activity like typing or uploading.
|
||||
Example:
|
||||
|
||||
```go
|
||||
func report(ctx *laniakea.MsgContext, db *App) error {
|
||||
func report(ctx *laniakea.MessageContext, db *App) error {
|
||||
ctx.SendAction(tgapi.ChatActionTyping)
|
||||
text, err := db.BuildReport(ctx.FromID)
|
||||
if err != nil {
|
||||
@@ -293,7 +293,7 @@ This is especially useful for slower handlers.
|
||||
A common pattern is:
|
||||
|
||||
```go
|
||||
func profile(ctx *laniakea.MsgContext, db *App) error {
|
||||
func profile(ctx *laniakea.MessageContext, db *App) error {
|
||||
user, err := db.LoadUser(ctx.FromID)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -338,7 +338,7 @@ Methods like `EditCallback(...)` and `AnswerCbQueryText(...)` depend on callback
|
||||
## A practical example
|
||||
|
||||
```go
|
||||
func settings(ctx *laniakea.MsgContext, db *App) error {
|
||||
func settings(ctx *laniakea.MessageContext, db *App) error {
|
||||
kb := ctx.NewInlineKeyboard(1).
|
||||
AddCallbackButton("Enable notifications", "settings.notifications.enable").
|
||||
AddCallbackButton("Disable notifications", "settings.notifications.disable")
|
||||
|
||||
+4
-4
@@ -1,6 +1,6 @@
|
||||
# Policies
|
||||
|
||||
Policies — это полноценная модель правил авторизации в Laniakea. Политика работает поверх нормализованного `MsgContext` и переданных `AppData`, возвращает `nil`, если доступ разрешён, и возвращает ошибку, если действие нужно запретить или сама проверка не смогла корректно выполниться.
|
||||
Policies — это полноценная модель правил авторизации в Laniakea. Политика работает поверх нормализованного `MessageContext` и переданных `AppData`, возвращает `nil`, если доступ разрешён, и возвращает ошибку, если действие нужно запретить или сама проверка не смогла корректно выполниться.
|
||||
|
||||
Policies не вводят вторую модель выполнения. Они встраиваются в уже существующий middleware pipeline через `RequirePolicy(...)`, поэтому авторизация остаётся на том же пути маршрутизации, что и остальной фреймворк.
|
||||
|
||||
@@ -15,7 +15,7 @@ Policies не вводят вторую модель выполнения. Он
|
||||
## Основной API
|
||||
|
||||
```go
|
||||
type Policy[T laniakea.AppData] func(ctx *laniakea.MsgContext, data T) error
|
||||
type Policy[T laniakea.AppData] func(ctx *laniakea.MessageContext, data T) error
|
||||
|
||||
func RequirePolicy[T laniakea.AppData](name string, p Policy[T]) Middleware[T]
|
||||
|
||||
@@ -62,7 +62,7 @@ plugin.UsePolicy(
|
||||
- `RequireBotAdmin(...)`
|
||||
- `RequireCallbackFromUser(...)`
|
||||
|
||||
Эти helpers используют нормализованные данные `MsgContext`. В частности, chat-aware политики опираются на `Chat` и `ChatID`, которые теперь заполняются не только для message-backed обновлений.
|
||||
Эти helpers используют нормализованные данные `MessageContext`. В частности, chat-aware политики опираются на `Chat` и `ChatID`, которые теперь заполняются не только для message-backed обновлений.
|
||||
|
||||
## Композиция
|
||||
|
||||
@@ -133,6 +133,6 @@ policy := laniakea.NotPolicy(laniakea.RequirePrivateChat())
|
||||
Связанные страницы:
|
||||
|
||||
- [[Middleware-RU]]
|
||||
- [[MsgContext-RU]]
|
||||
- [[MessageContext-RU]]
|
||||
- [[Commands-and-Plugins-RU]]
|
||||
- [[Framework-Backlog-RU]]
|
||||
|
||||
+4
-4
@@ -1,6 +1,6 @@
|
||||
# Policies
|
||||
|
||||
Policies are Laniakea's first-class authorization rules. A policy runs against the normalized `MsgContext` and injected `AppData`, returns `nil` when access is allowed, and returns an error when access should be denied or when the check itself fails.
|
||||
Policies are Laniakea's first-class authorization rules. A policy runs against the normalized `MessageContext` and injected `AppData`, returns `nil` when access is allowed, and returns an error when access should be denied or when the check itself fails.
|
||||
|
||||
Policies do not introduce a second execution model. They plug into the existing middleware pipeline through `RequirePolicy(...)`, so authorization stays on the same routing path as the rest of the framework.
|
||||
|
||||
@@ -15,7 +15,7 @@ Policies do not introduce a second execution model. They plug into the existing
|
||||
## Core API
|
||||
|
||||
```go
|
||||
type Policy[T laniakea.AppData] func(ctx *laniakea.MsgContext, data T) error
|
||||
type Policy[T laniakea.AppData] func(ctx *laniakea.MessageContext, data T) error
|
||||
|
||||
func RequirePolicy[T laniakea.AppData](name string, p Policy[T]) Middleware[T]
|
||||
|
||||
@@ -62,7 +62,7 @@ The current built-in helpers focus on common Telegram-specific access checks:
|
||||
- `RequireBotAdmin(...)`
|
||||
- `RequireCallbackFromUser(...)`
|
||||
|
||||
These helpers use normalized `MsgContext` data. In particular, chat-aware policies rely on `Chat` and `ChatID`, which are now populated for more update kinds than only message-backed ones.
|
||||
These helpers use normalized `MessageContext` data. In particular, chat-aware policies rely on `Chat` and `ChatID`, which are now populated for more update kinds than only message-backed ones.
|
||||
|
||||
## Composition
|
||||
|
||||
@@ -133,6 +133,6 @@ Those features can be added later if the existing `Policy[T]` model proves too s
|
||||
Related pages:
|
||||
|
||||
- [[Middleware]]
|
||||
- [[MsgContext]]
|
||||
- [[MessageContext]]
|
||||
- [[Commands-and-Plugins]]
|
||||
- [[Framework-Backlog]]
|
||||
|
||||
+9
-9
@@ -14,11 +14,11 @@ type App struct{}
|
||||
func (a *App) IsAdmin(userID int64) bool { return userID == 42 }
|
||||
|
||||
admin := laniakea.NewPlugin[*App]("admin")
|
||||
admin.AddMiddleware(laniakea.NewMiddleware("admin-only", func(ctx *laniakea.MsgContext, app *App) bool {
|
||||
admin.AddMiddleware(laniakea.NewMiddleware("admin-only", func(ctx *laniakea.MessageContext, app *App) bool {
|
||||
return ctx.From != nil && app.IsAdmin(ctx.From.ID)
|
||||
}))
|
||||
|
||||
admin.NewCommand(func(ctx *laniakea.MsgContext, app *App) error {
|
||||
admin.NewCommand(func(ctx *laniakea.MessageContext, app *App) error {
|
||||
ctx.Answer("Admin command executed")
|
||||
return nil
|
||||
}, "reload")
|
||||
@@ -31,14 +31,14 @@ 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.NewCommand(func(ctx *laniakea.MessageContext, 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.NewPayload(func(ctx *laniakea.MessageContext, db laniakea.NoData) error {
|
||||
ctx.EditCallback("Settings screen", nil)
|
||||
return nil
|
||||
}, "settings")
|
||||
@@ -49,7 +49,7 @@ 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.NewCommand(func(ctx *laniakea.MessageContext, db *App) error {
|
||||
report := buildLargePlainTextReport()
|
||||
ctx.AnswerLong(report)
|
||||
return nil
|
||||
@@ -71,7 +71,7 @@ l10n := laniakea.NewL10n("en").
|
||||
|
||||
bot.SetL10n(l10n)
|
||||
|
||||
plugin.NewCommand(func(ctx *laniakea.MsgContext, db *App) error {
|
||||
plugin.NewCommand(func(ctx *laniakea.MessageContext, db *App) error {
|
||||
ctx.Answer(ctx.Translate("greeting"))
|
||||
return nil
|
||||
}, "start")
|
||||
@@ -82,7 +82,7 @@ plugin.NewCommand(func(ctx *laniakea.MsgContext, db *App) error {
|
||||
Use `AddUpdateHandler(...)` for Telegram update types that are outside the command and payload flow.
|
||||
|
||||
```go
|
||||
plugin.AddUpdateHandler(tgapi.UpdateTypeInlineQuery, func(ctx *laniakea.MsgContext, db *App) error {
|
||||
plugin.AddUpdateHandler(tgapi.UpdateTypeInlineQuery, func(ctx *laniakea.MessageContext, db *App) error {
|
||||
if ctx.From != nil {
|
||||
ctx.Logger.Infoln("inline query from", ctx.From.ID)
|
||||
}
|
||||
@@ -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.NewCommand(func(ctx *laniakea.MessageContext, db *App) error {
|
||||
draft := ctx.NewDraft()
|
||||
if draft == nil {
|
||||
return nil
|
||||
@@ -119,7 +119,7 @@ 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 {
|
||||
plugin.NewCommand(func(ctx *laniakea.MessageContext, db *App) error {
|
||||
uploader := tgapi.NewUploader(ctx.Api)
|
||||
defer uploader.Close()
|
||||
|
||||
|
||||
+50
-16
@@ -22,43 +22,59 @@ Runner — это фоновая или одноразовая задача, к
|
||||
runner := laniakea.NewRunner("cleanup", fn)
|
||||
```
|
||||
|
||||
Потом конфигурируются методы builder:
|
||||
- `Onetime(bool)`
|
||||
- `Async(bool)`
|
||||
- `Timeout(duration)`
|
||||
Методы builder:
|
||||
- `Every(time.Duration)` — интервал повторного запуска. Ноль (по умолчанию) означает одноразовый запуск; положительное значение — периодический.
|
||||
- `Async(bool)` — если `true` (по умолчанию), запускается в goroutine; если `false`, блокирует запуск runtime.
|
||||
|
||||
## Основные режимы
|
||||
|
||||
### Одноразовый sync
|
||||
### Одноразовый async (по умолчанию)
|
||||
|
||||
- выполняется один раз;
|
||||
- блокирует запуск;
|
||||
- полезен для работы, критичной на старте.
|
||||
|
||||
### Одноразовый async
|
||||
```go
|
||||
runner := laniakea.NewRunner("prefetch", fn)
|
||||
```
|
||||
|
||||
- выполняется один раз;
|
||||
- стартует в goroutine;
|
||||
- не блокирует запуск.
|
||||
|
||||
### Одноразовый sync
|
||||
|
||||
```go
|
||||
runner := laniakea.NewRunner("warmup", fn).Async(false)
|
||||
```
|
||||
|
||||
- выполняется один раз;
|
||||
- блокирует запуск до завершения;
|
||||
- полезен для работы, критичной на старте.
|
||||
|
||||
### Повторяющийся async
|
||||
|
||||
- работает циклически;
|
||||
```go
|
||||
runner := laniakea.NewRunner("cleanup", fn).Every(time.Minute)
|
||||
```
|
||||
|
||||
- работает циклически с заданным интервалом;
|
||||
- использует ticker;
|
||||
- живет до `ctx.Done()`.
|
||||
|
||||
## Невалидная конфигурация
|
||||
|
||||
Повторяющийся synchronous runner считается невалидным и пропускается с предупреждением.
|
||||
|
||||
Также повторяющийся async runner без `Timeout(...)` пропускается.
|
||||
Повторяющийся synchronous runner (`Every(d > 0).Async(false)`) считается невалидным и пропускается с предупреждением — блокировать запуск бесконечно никогда не имеет смысла.
|
||||
|
||||
## Когда стартуют фоновые задачи
|
||||
|
||||
Фоновые задачи стартуют из `RunWithContext(...)` или `RunWebHookWithContext(...)`, а не из `NewBot(...)`.
|
||||
Фоновые задачи стартуют из `RunWithContext(...)` или `RunWebhookWithContext(...)`, а не из `NewBot(...)`.
|
||||
|
||||
Это часть фазы выполнения, а не фазы сборки конфигурации.
|
||||
|
||||
## Обработка ошибок
|
||||
|
||||
Если runner возвращает ошибку:
|
||||
- бот логирует предупреждение;
|
||||
- через observer отправляется `ErrorEvent`;
|
||||
- бот продолжает работу.
|
||||
|
||||
## Семантика остановки
|
||||
|
||||
При корректной остановке бот:
|
||||
@@ -67,9 +83,27 @@ runner := laniakea.NewRunner("cleanup", fn)
|
||||
|
||||
Поэтому код runner должен завершаться достаточно быстро.
|
||||
|
||||
## Практические примеры
|
||||
|
||||
### Периодическая очистка
|
||||
|
||||
```go
|
||||
cleanup := laniakea.NewRunner("cleanup", func(bot *laniakea.Bot[*App]) error {
|
||||
return bot.GetAppData().CleanupExpired()
|
||||
}).Every(5 * time.Minute)
|
||||
```
|
||||
|
||||
### Блокирующий запуск при старте
|
||||
|
||||
```go
|
||||
warmup := laniakea.NewRunner("warmup", func(bot *laniakea.Bot[*App]) error {
|
||||
return bot.GetAppData().WarmCaches()
|
||||
}).Async(false)
|
||||
```
|
||||
|
||||
## Рекомендации
|
||||
|
||||
- Для периодических задач используй повторяющийся async runner с timeout.
|
||||
- Для периодических задач используй повторяющийся async runner с `Every(...)`.
|
||||
- Для критичной стартовой работы используй одноразовый sync runner.
|
||||
- Не держи сложную бизнес-логику внутри runner; лучше делегируй ее в обычные сервисы приложения.
|
||||
|
||||
|
||||
+39
-49
@@ -13,10 +13,9 @@ Each runner is built from:
|
||||
- a function `func(*Bot[T]) error`;
|
||||
- execution flags configured through builder methods.
|
||||
|
||||
Main builder methods:
|
||||
- `Onetime(bool)`
|
||||
- `Async(bool)`
|
||||
- `Timeout(duration)`
|
||||
Builder methods:
|
||||
- `Every(time.Duration)` — sets the repeat interval. Zero (default) means run once; positive means repeat.
|
||||
- `Async(bool)` — if `true` (default), the runner runs in a goroutine; if `false`, it blocks runtime startup.
|
||||
|
||||
## Creating a runner
|
||||
|
||||
@@ -29,36 +28,21 @@ cleanup := laniakea.NewRunner("cleanup", func(bot *laniakea.Bot[*App]) error {
|
||||
```
|
||||
|
||||
By default, a new runner is:
|
||||
- asynchronous;
|
||||
- not one-time;
|
||||
- configured with zero timeout.
|
||||
- asynchronous (`Async(true)`);
|
||||
- one-shot (`Every(0)`).
|
||||
|
||||
That default means you almost always want to finish configuration before adding it to the bot.
|
||||
That default fires the runner once in a goroutine when the bot starts.
|
||||
|
||||
## Runner execution modes
|
||||
|
||||
There are three meaningful configurations.
|
||||
|
||||
### One-time synchronous
|
||||
### One-time asynchronous (default)
|
||||
|
||||
```go
|
||||
runner := laniakea.NewRunner("warmup", fn).
|
||||
Onetime(true).
|
||||
Async(false)
|
||||
```
|
||||
|
||||
Behavior:
|
||||
- runs once;
|
||||
- blocks startup until it finishes;
|
||||
- logs a warning if it takes longer than two seconds.
|
||||
|
||||
Use this for startup work that must complete before the bot is considered ready.
|
||||
|
||||
### One-time asynchronous
|
||||
|
||||
```go
|
||||
runner := laniakea.NewRunner("prefetch", fn).
|
||||
Onetime(true)
|
||||
runner := laniakea.NewRunner("prefetch", fn)
|
||||
// or explicitly:
|
||||
runner := laniakea.NewRunner("prefetch", fn).Every(0).Async(true)
|
||||
```
|
||||
|
||||
Behavior:
|
||||
@@ -68,31 +52,37 @@ Behavior:
|
||||
|
||||
Use this for fire-and-forget startup work that is useful but not required before handling updates.
|
||||
|
||||
### Repeating asynchronous
|
||||
### One-time synchronous
|
||||
|
||||
```go
|
||||
runner := laniakea.NewRunner("cleanup", fn).
|
||||
Timeout(time.Minute)
|
||||
runner := laniakea.NewRunner("warmup", fn).Async(false)
|
||||
```
|
||||
|
||||
Behavior:
|
||||
- runs on a ticker;
|
||||
- runs once;
|
||||
- blocks startup until it finishes;
|
||||
- logs a warning if it takes longer than two seconds.
|
||||
|
||||
Use this for startup work that must complete before the bot is considered ready.
|
||||
|
||||
### Repeating asynchronous
|
||||
|
||||
```go
|
||||
runner := laniakea.NewRunner("cleanup", fn).Every(time.Minute)
|
||||
```
|
||||
|
||||
Behavior:
|
||||
- runs on a ticker with the configured interval;
|
||||
- keeps running until `ctx.Done()` from the bot runtime context;
|
||||
- is awaited during graceful shutdown.
|
||||
|
||||
Use this for recurring background jobs.
|
||||
|
||||
## Invalid configuration
|
||||
## Invalid configurations
|
||||
|
||||
One configuration is intentionally treated as invalid:
|
||||
One configuration is intentionally treated as invalid and skipped with a warning:
|
||||
|
||||
- `Onetime(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.
|
||||
- `Every(d > 0).Async(false)` — a periodic sync runner blocks startup indefinitely, which is never correct.
|
||||
|
||||
## Registration
|
||||
|
||||
@@ -106,7 +96,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.
|
||||
|
||||
@@ -125,6 +115,7 @@ Runner functions return `error`.
|
||||
|
||||
When a runner returns a non-nil error:
|
||||
- the bot logs a warning;
|
||||
- an `ErrorEvent` is emitted through the observer;
|
||||
- the process continues;
|
||||
- the bot does not crash automatically.
|
||||
|
||||
@@ -134,7 +125,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,15 +140,15 @@ 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
|
||||
### Startup warmup (blocking)
|
||||
|
||||
```go
|
||||
warmup := laniakea.NewRunner("warmup", func(bot *laniakea.Bot[*App]) error {
|
||||
return bot.GetAppData().WarmCaches()
|
||||
}).Onetime(true).Async(false)
|
||||
}).Async(false)
|
||||
```
|
||||
|
||||
### Background metrics push
|
||||
@@ -165,25 +156,24 @@ 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.
|
||||
|
||||
## Caveats
|
||||
|
||||
- Runners do not receive `context.Context` directly; they receive `*Bot[T]`.
|
||||
- Repeating sync runners are skipped.
|
||||
- Repeating async runners without timeout are skipped.
|
||||
- Periodic sync runners are skipped with a warning.
|
||||
- Slow one-time sync runners delay bot startup.
|
||||
|
||||
## Related pages
|
||||
|
||||
- [[Bot-Lifecycle]]
|
||||
- [[Testing-Bots-with-Laniakea]]
|
||||
- [[Testing-Bots-with-Laniakea]]
|
||||
+6
-6
@@ -1,11 +1,11 @@
|
||||
# Scenes
|
||||
|
||||
Scenes — это слой маршрутизации Laniakea с сохранением состояния для многошаговых и модальных диалогов. Сцена регистрируется внутри плагина, запускается через `MsgContext`, хранится через `SessionStore` и получает обновления раньше обычной маршрутизации команд, пока её сессия активна.
|
||||
Scenes — это слой маршрутизации Laniakea с сохранением состояния для многошаговых и модальных диалогов. Сцена регистрируется внутри плагина, запускается через `MessageContext`, хранится через `SessionStore` и получает обновления раньше обычной маршрутизации команд, пока её сессия активна.
|
||||
|
||||
## Что дают сцены
|
||||
|
||||
- Регистрацию через `Plugin.NewScene(...)` и `Plugin.AddScene(...)`.
|
||||
- Явный вход и выход через `MsgContext.EnterScene(...)`, `EnterSceneStep(...)` и `ExitScene()`.
|
||||
- Явный вход и выход через `MessageContext.EnterScene(...)`, `EnterSceneStep(...)` и `ExitScene()`.
|
||||
- Области действия сессии на пользователя, чат или пару пользователь-чат.
|
||||
- Обработчики шагов, локальные команды сцены и резервный обработчик сообщений на уровне сцены.
|
||||
- JSON-состояние сцены через `SceneContext.BindData(...)` и `SaveData(...)`.
|
||||
@@ -67,13 +67,13 @@ plugin.NewScene("signup").
|
||||
|
||||
## Модель обработчиков
|
||||
|
||||
Обычные команды используют `*MsgContext`. Обработчики сцен используют `*SceneContext`.
|
||||
Обычные команды используют `*MessageContext`. Обработчики сцен используют `*SceneContext`.
|
||||
|
||||
```go
|
||||
type SceneHandler[T any] func(ctx *SceneContext, db T) (SceneResult, error)
|
||||
```
|
||||
|
||||
`SceneContext` встраивает `*MsgContext` и добавляет вспомогательные методы для сцен:
|
||||
`SceneContext` встраивает `*MessageContext` и добавляет вспомогательные методы для сцен:
|
||||
|
||||
- `ctx.Stay()`
|
||||
- `ctx.Next(step)`
|
||||
@@ -137,7 +137,7 @@ func askName(ctx *laniakea.SceneContext, db *App) (laniakea.SceneResult, error)
|
||||
Точка входа из команды:
|
||||
|
||||
```go
|
||||
func startSignup(ctx *laniakea.MsgContext, db *App) error {
|
||||
func startSignup(ctx *laniakea.MessageContext, db *App) error {
|
||||
return ctx.EnterScene("signup")
|
||||
}
|
||||
```
|
||||
@@ -174,6 +174,6 @@ func cancelSignup(ctx *laniakea.SceneContext, db *App) (laniakea.SceneResult, er
|
||||
Связанные страницы:
|
||||
|
||||
- [[Commands-and-Plugins-RU]]
|
||||
- [[MsgContext-RU]]
|
||||
- [[MessageContext-RU]]
|
||||
- [[Middleware-RU]]
|
||||
- [[Bot-Lifecycle-RU]]
|
||||
|
||||
+6
-6
@@ -1,11 +1,11 @@
|
||||
# Scenes
|
||||
|
||||
Scenes are Laniakea's stateful routing layer for multi-step and modal bot flows. A scene is registered inside a plugin, entered through `MsgContext`, stored through `SessionStore`, and routed before normal command handling while the session is active.
|
||||
Scenes are Laniakea's stateful routing layer for multi-step and modal bot flows. A scene is registered inside a plugin, entered through `MessageContext`, stored through `SessionStore`, and routed before normal command handling while the session is active.
|
||||
|
||||
## What scenes give you
|
||||
|
||||
- Scene registration through `Plugin.NewScene(...)` and `Plugin.AddScene(...)`.
|
||||
- Explicit entry and exit through `MsgContext.EnterScene(...)`, `EnterSceneStep(...)`, and `ExitScene()`.
|
||||
- Explicit entry and exit through `MessageContext.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.
|
||||
- JSON-backed scene state through `SceneContext.BindData(...)` and `SaveData(...)`.
|
||||
@@ -67,13 +67,13 @@ plugin.NewScene("signup").
|
||||
|
||||
## Handler model
|
||||
|
||||
Normal commands use `*MsgContext`. Scene handlers use `*SceneContext`.
|
||||
Normal commands use `*MessageContext`. Scene handlers use `*SceneContext`.
|
||||
|
||||
```go
|
||||
type SceneHandler[T any] func(ctx *SceneContext, db T) (SceneResult, error)
|
||||
```
|
||||
|
||||
`SceneContext` embeds `*MsgContext` and adds scene helpers:
|
||||
`SceneContext` embeds `*MessageContext` and adds scene helpers:
|
||||
|
||||
- `ctx.Stay()`
|
||||
- `ctx.Next(step)`
|
||||
@@ -137,7 +137,7 @@ The store contract stays intentionally small because `Data []byte` is storage-ag
|
||||
Command entry:
|
||||
|
||||
```go
|
||||
func startSignup(ctx *laniakea.MsgContext, db *App) error {
|
||||
func startSignup(ctx *laniakea.MessageContext, db *App) error {
|
||||
return ctx.EnterScene("signup")
|
||||
}
|
||||
```
|
||||
@@ -174,6 +174,6 @@ func cancelSignup(ctx *laniakea.SceneContext, db *App) (laniakea.SceneResult, er
|
||||
Related pages:
|
||||
|
||||
- [[Commands-and-Plugins]]
|
||||
- [[MsgContext]]
|
||||
- [[MessageContext]]
|
||||
- [[Middleware]]
|
||||
- [[Bot-Lifecycle]]
|
||||
|
||||
@@ -10,7 +10,7 @@ Laniakea хорошо тестируется обычными Go unit tests.
|
||||
|
||||
В репозитории уже используются паттерны вроде:
|
||||
- fake HTTP transport для `tgapi`;
|
||||
- прямые тесты для вспомогательных методов `MsgContext`;
|
||||
- прямые тесты для вспомогательных методов `MessageContext`;
|
||||
- routing tests для commands и payloads;
|
||||
- runner tests.
|
||||
|
||||
@@ -35,7 +35,7 @@ Laniakea хорошо тестируется обычными Go unit tests.
|
||||
## Тесты для логики обработчиков
|
||||
|
||||
Для тестов уровня обработчика обычно полезно:
|
||||
- собрать `MsgContext`;
|
||||
- собрать `MessageContext`;
|
||||
- вызвать обработчик напрямую;
|
||||
- проверить побочные эффекты и ответы.
|
||||
|
||||
@@ -63,5 +63,5 @@ Laniakea хорошо тестируется обычными Go unit tests.
|
||||
## Что читать дальше
|
||||
|
||||
- [[Runners-RU]]
|
||||
- [[MsgContext-RU]]
|
||||
- [[MessageContext-RU]]
|
||||
- [[Testing-Bots-with-Laniakea]]
|
||||
|
||||
@@ -21,15 +21,15 @@ Good test targets include:
|
||||
The most practical approach is:
|
||||
|
||||
1. isolate one behavior;
|
||||
2. create a small bot, plugin, or `MsgContext`;
|
||||
2. create a small bot, plugin, or `MessageContext`;
|
||||
3. use a fake HTTP client when you need to inspect Telegram requests;
|
||||
4. assert the outgoing request shape or returned behavior directly.
|
||||
|
||||
This keeps tests fast and independent from real Telegram infrastructure.
|
||||
|
||||
## Testing `MsgContext` helpers
|
||||
## Testing `MessageContext` helpers
|
||||
|
||||
Many helper methods can be tested by constructing a `MsgContext` directly.
|
||||
Many helper methods can be tested by constructing a `MessageContext` directly.
|
||||
|
||||
Common ingredients:
|
||||
- a fake `tgapi.API` with a custom `http.Client`;
|
||||
@@ -39,7 +39,7 @@ Common ingredients:
|
||||
Example pattern:
|
||||
|
||||
```go
|
||||
ctx := &laniakea.MsgContext{
|
||||
ctx := &laniakea.MessageContext{
|
||||
Api: api,
|
||||
Msg: &tgapi.Message{Chat: &tgapi.Chat{ID: 42, Type: string(tgapi.ChatTypePrivate)}},
|
||||
Logger: slog.CreateLogger(),
|
||||
@@ -64,7 +64,7 @@ This is especially useful for:
|
||||
- split long-message requests;
|
||||
- keyboard attachment behavior.
|
||||
|
||||
The repository already uses this pattern extensively for `MsgContext` helper tests.
|
||||
The repository already uses this pattern extensively for `MessageContext` helper tests.
|
||||
|
||||
## Testing long replies
|
||||
|
||||
@@ -96,7 +96,7 @@ For routing tests, create a small bot with:
|
||||
|
||||
Then call the bot's handling path in a focused test and assert:
|
||||
- which handler was called;
|
||||
- what `MsgContext` fields were populated;
|
||||
- what `MessageContext` fields were populated;
|
||||
- whether context mutations leaked across plugins.
|
||||
|
||||
This is particularly useful for:
|
||||
@@ -184,4 +184,4 @@ When you fix a bug, consider adding a test for:
|
||||
- [[Commands-and-Plugins]]
|
||||
- [[Error-Handling]]
|
||||
- [[Runners]]
|
||||
- [[MsgContext]]
|
||||
- [[MessageContext]]
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
English version: [[Update-Routing-Model]]
|
||||
|
||||
Эта страница фиксирует текущий контракт маршрутизации обновлений в Laniakea: какие Telegram update types идут через команды, какие через данные callback, какие через generic update handlers, и какие гарантии по `MsgContext` существуют в каждом потоке.
|
||||
Эта страница фиксирует текущий контракт маршрутизации обновлений в Laniakea: какие Telegram update types идут через команды, какие через данные callback, какие через generic update handlers, и какие гарантии по `MessageContext` существуют в каждом потоке.
|
||||
|
||||
Это описание текущего поведения фреймворка, а не отдельная новая API-поверхность. Цель страницы — сделать уже существующую маршрутизацию и нормализацию `MsgContext` явной и тестируемой.
|
||||
Это описание текущего поведения фреймворка, а не отдельная новая API-поверхность. Цель страницы — сделать уже существующую маршрутизацию и нормализацию `MessageContext` явной и тестируемой.
|
||||
|
||||
## Верхнеуровневая маршрутизация
|
||||
|
||||
@@ -23,7 +23,7 @@ English version: [[Update-Routing-Model]]
|
||||
|
||||
## Что делает `prepareUpdateCtx(...)`
|
||||
|
||||
До маршрутизации обработчиков Laniakea нормализует `MsgContext` из входящего Telegram update.
|
||||
До маршрутизации обработчиков Laniakea нормализует `MessageContext` из входящего Telegram update.
|
||||
|
||||
Эта нормализация намеренно шире, чем command и payload routing:
|
||||
- некоторые update types заполняют `ctx.Msg`
|
||||
@@ -101,12 +101,12 @@ Generic update handlers регистрируются через `Plugin.AddUpdat
|
||||
- `ctx.Text` не нормализуется
|
||||
- `ctx.Args` не нормализуется
|
||||
- `ctx.Prefix` не нормализуется
|
||||
- каждый plugin update handler получает свою копию `MsgContext` struct
|
||||
- каждый plugin update handler получает свою копию `MessageContext` struct
|
||||
|
||||
Важное ограничение:
|
||||
- копируется сам `MsgContext`, но не обещается глубокая копия всех вложенных Telegram-структур
|
||||
- копируется сам `MessageContext`, но не обещается глубокая копия всех вложенных Telegram-структур
|
||||
|
||||
## Нормализованные поля `MsgContext` по видам update
|
||||
## Нормализованные поля `MessageContext` по видам update
|
||||
|
||||
### Update types, которые несут message
|
||||
|
||||
@@ -176,6 +176,6 @@ Generic update handlers регистрируются через `Plugin.AddUpdat
|
||||
## Связанные страницы
|
||||
|
||||
- [[Commands-and-Plugins-RU]]
|
||||
- [[MsgContext-RU]]
|
||||
- [[MessageContext-RU]]
|
||||
- [[Scenes-RU]]
|
||||
- [[Bot-Lifecycle-RU]]
|
||||
|
||||
+6
-6
@@ -2,9 +2,9 @@
|
||||
|
||||
Russian version: [[Update-Routing-Model-RU]]
|
||||
|
||||
This page defines the current update-routing contract in Laniakea: which Telegram update kinds go through command routing, which go through payload routing, which go through generic update handlers, and what `MsgContext` guarantees exist in each path.
|
||||
This page defines the current update-routing contract in Laniakea: which Telegram update kinds go through command routing, which go through payload routing, which go through generic update handlers, and what `MessageContext` guarantees exist in each path.
|
||||
|
||||
This is a model of current framework behavior, not a second API surface. The goal is to make the existing routing and `MsgContext` normalization explicit and testable.
|
||||
This is a model of current framework behavior, not a second API surface. The goal is to make the existing routing and `MessageContext` normalization explicit and testable.
|
||||
|
||||
## Top-level routing
|
||||
|
||||
@@ -23,7 +23,7 @@ Important:
|
||||
|
||||
## What `prepareUpdateCtx(...)` does
|
||||
|
||||
Before handler routing, Laniakea normalizes a `MsgContext` from the incoming Telegram update.
|
||||
Before handler routing, Laniakea normalizes a `MessageContext` from the incoming Telegram update.
|
||||
|
||||
That normalization is intentionally broader than command and payload routing:
|
||||
- some update kinds populate `ctx.Msg`
|
||||
@@ -101,12 +101,12 @@ General guarantees:
|
||||
- `ctx.Text` is not normalized
|
||||
- `ctx.Args` is not normalized
|
||||
- `ctx.Prefix` is not normalized
|
||||
- each plugin update handler receives its own copied `MsgContext` struct
|
||||
- each plugin update handler receives its own copied `MessageContext` struct
|
||||
|
||||
Important limitation:
|
||||
- the copied context is an isolated struct copy, not a deep copy of all nested Telegram payload objects
|
||||
|
||||
## Normalized `MsgContext` fields by update kind
|
||||
## Normalized `MessageContext` fields by update kind
|
||||
|
||||
### Message-backed update kinds
|
||||
|
||||
@@ -176,6 +176,6 @@ Important:
|
||||
## Related pages
|
||||
|
||||
- [[Commands-and-Plugins]]
|
||||
- [[MsgContext]]
|
||||
- [[MessageContext]]
|
||||
- [[Scenes]]
|
||||
- [[Bot-Lifecycle]]
|
||||
|
||||
+14
-14
@@ -2,10 +2,10 @@
|
||||
|
||||
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`;
|
||||
- текущий публичный API использует историческое написание `WebHook` в идентификаторах вроде `RunWebhookWithContext(...)`, `RunWebhook(...)` и `BotWebhookOpts`;
|
||||
- в тексте страницы используется обычное слово "webhook", но в примерах остаются реальные имена 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.
|
||||
|
||||
## Частые ошибки
|
||||
|
||||
+14
-14
@@ -2,10 +2,10 @@
|
||||
|
||||
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`;
|
||||
- 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.
|
||||
|
||||
## 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
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
- [[Getting-Started]]
|
||||
- [[Bot-Options-and-Configuration]]
|
||||
- [[Commands-and-Plugins]]
|
||||
- [[MsgContext]]
|
||||
- [[MessageContext]]
|
||||
|
||||
## Runtime and Architecture
|
||||
- [[Bot-Lifecycle]]
|
||||
|
||||
+4
-4
@@ -21,14 +21,14 @@ English version: [[tgapi-Overview]]
|
||||
|
||||
Это разделение специально сделано, чтобы JSON methods и file upload methods не смешивались в одну слишком размытую abstraction.
|
||||
|
||||
## Когда использовать `MsgContext`, а когда `tgapi`
|
||||
## Когда использовать `MessageContext`, а когда `tgapi`
|
||||
|
||||
Используй `MsgContext`, когда:
|
||||
Используй `MessageContext`, когда:
|
||||
- ты уже внутри handler'а;
|
||||
- нужен обычный поток reply/edit/delete/callback.
|
||||
|
||||
Используй `tgapi`, когда:
|
||||
- у `MsgContext` нет нужного вспомогательного метода;
|
||||
- у `MessageContext` нет нужного вспомогательного метода;
|
||||
- ты работаешь вне потока обработчика;
|
||||
- нужен более низкоуровневый контроль;
|
||||
- нужно работать с uploads/downloads напрямую.
|
||||
@@ -100,7 +100,7 @@ _, err := uploader.SendPhoto(tgapi.UploadPhoto{
|
||||
|
||||
## Что читать дальше
|
||||
|
||||
- [[MsgContext-RU]]
|
||||
- [[MessageContext-RU]]
|
||||
- [[Inline-Keyboards-and-Payloads-RU]]
|
||||
- [[Rate-Limiting-RU]]
|
||||
- [[tgapi-Overview]]
|
||||
|
||||
+6
-6
@@ -2,7 +2,7 @@
|
||||
|
||||
Russian version: [[tgapi-Overview-RU]]
|
||||
|
||||
`tgapi` is the low-level Telegram Bot API layer used under Laniakea’s higher-level bot runtime. Use it when you need direct access to Telegram methods, explicit parameter structs, upload control, or raw request building that sits below plugins and `MsgContext` helpers.
|
||||
`tgapi` is the low-level Telegram Bot API layer used under Laniakea’s higher-level bot runtime. Use it when you need direct access to Telegram methods, explicit parameter structs, upload control, or raw request building that sits below plugins and `MessageContext` helpers.
|
||||
|
||||
## The important split first
|
||||
|
||||
@@ -20,7 +20,7 @@ If you stay on the typed method surface, you usually do not need to think about
|
||||
## The normal layering
|
||||
|
||||
In practice, Laniakea has three levels:
|
||||
- high-level handler helpers on `MsgContext`;
|
||||
- high-level handler helpers on `MessageContext`;
|
||||
- runtime structure on `Bot`, plugins, and middleware;
|
||||
- low-level Telegram access in `tgapi`.
|
||||
|
||||
@@ -29,7 +29,7 @@ In practice, Laniakea has three levels:
|
||||
## When to use `tgapi` directly
|
||||
|
||||
Use `tgapi` directly when:
|
||||
- a `MsgContext` helper does not expose the Telegram feature you need;
|
||||
- a `MessageContext` helper does not expose the Telegram feature you need;
|
||||
- you need a Telegram method outside the high-level command/payload flow;
|
||||
- you want explicit control over params, parse modes, message edits, or uploads;
|
||||
- you are writing infrastructure code rather than command logic.
|
||||
@@ -43,7 +43,7 @@ Typical examples where `tgapi` is the better tool:
|
||||
- setting bot metadata or command scopes directly;
|
||||
- file downloads and streaming;
|
||||
- multipart uploads;
|
||||
- one-off Telegram methods that do not have a `MsgContext` wrapper.
|
||||
- one-off Telegram methods that do not have a `MessageContext` wrapper.
|
||||
|
||||
## Typed methods first
|
||||
|
||||
@@ -244,7 +244,7 @@ Related page:
|
||||
|
||||
As a rule of thumb:
|
||||
|
||||
- use `MsgContext` when responding to the current update;
|
||||
- use `MessageContext` when responding to the current update;
|
||||
- use `Bot` and plugins when structuring runtime behavior;
|
||||
- use `tgapi` when you need direct Telegram method control;
|
||||
- use raw `NewRequest` or `NewUploaderRequest` only as the final fallback.
|
||||
@@ -254,7 +254,7 @@ That boundary keeps normal bot code ergonomic without hiding Telegram-specific c
|
||||
## Related pages
|
||||
|
||||
- [[Getting-Started]] for the normal high-level bot setup path.
|
||||
- [[MsgContext]] for handler-time reply helpers.
|
||||
- [[MessageContext]] for handler-time reply helpers.
|
||||
- [[Inline-Keyboards-and-Payloads]] for callback button construction.
|
||||
- [[Bot-Lifecycle]] for runtime startup and shutdown responsibilities around `Bot`, `API`, and `Uploader`.
|
||||
- [[Rate-Limiting]] for limiter and `retry_after` behavior.
|
||||
|
||||
Reference in New Issue
Block a user