# Vendelieu Telegram Bot Build Wizard Flow

> Build Wizard Flow

- Skill: `tomevault-io/vendelieu-telegram-bot-build-wizard-flow` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add tomevault-io/vendelieu-telegram-bot-build-wizard-flow`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tomevault-io/vendelieu-telegram-bot-build-wizard-flow/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: tomevault-io (https://skillmd.com/u/tomevault-io)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/tomevault-io/vendelieu-telegram-bot-build-wizard-flow

---


# Build Wizard Flow

## Quick Start

Annotate an object/class with `@WizardHandler`. Define nested steps extending `WizardStep`. KSP generates the `WizardActivity`; no manual Activity class needed.

```kotlin
import eu.vendeli.tgbot.types.component.MessageUpdate

@WizardHandler(trigger = ["/start"])
object RegistrationWizard {
    object NameStep : WizardStep(isInitial = true) {
        override suspend fun onEntry(ctx: WizardContext) {
            message("What's your name?").send(ctx.user, ctx.bot)
        }
        override suspend fun validate(ctx: WizardContext): Transition {
            val text = (ctx.update as? MessageUpdate)?.message?.text ?: return Transition.Retry()
            return if (text.isNotBlank()) Transition.Next else Transition.Retry("Invalid name")
        }
        override suspend fun store(ctx: WizardContext): String? =
            (ctx.update as? MessageUpdate)?.message?.text
    }

    object AgeStep : WizardStep() {
        override suspend fun onEntry(ctx: WizardContext) {
            message("How old are you?").send(ctx.user, ctx.bot)
        }
        override suspend fun validate(ctx: WizardContext): Transition {
            val age = (ctx.update as? MessageUpdate)?.message?.text?.toIntOrNull()
            return if (age != null && age in 1..150) Transition.Next else Transition.Retry()
        }
        override suspend fun store(ctx: WizardContext): Int? =
            (ctx.update as? MessageUpdate)?.message?.text?.toIntOrNull()
    }
}
```

## WizardStep Lifecycle

- **onEntry(ctx)** — Called when entering the step. Send prompts, set keyboards.
- **validate(ctx)** — Return `Transition` based on input.
- **store(ctx)** — Return value to persist (or null). Type must match a state manager.
- **onRetry(ctx, reason)** — Called when validation fails (optional).

## Transitions

| Transition | Effect |
|------------|--------|
| `Transition.Next` | Move to next step in sequence |
| `Transition.JumpTo(Step::class)` | Jump to specific step |
| `Transition.Retry()` or `Transition.Retry("reason")` | Stay on step, call onRetry |
| `Transition.Finish` | End wizard |

## State Managers

Default: `MapStringStateManager`, `MapIntStateManager`, `MapLongStateManager`. KSP matches `store()` return type to the manager.

Override per step:

```kotlin
@WizardHandler.StateManager(CustomStateManager::class)
object CustomStep : WizardStep { ... }
```

## WizardContext

- `ctx.user`, `ctx.update`, `ctx.bot`
- `ctx.getState(Step::class)`, `ctx.setState(Step::class, value)`, `ctx.delState(Step::class)` — type-safe accessors generated by KSP for each step

## Trigger and Scope

- `trigger = ["/start", "/register"]` — commands that start the wizard
- `scope = [UpdateType.MESSAGE]` — default; use `UpdateType.CALLBACK_QUERY` for button-triggered wizards

## Related Skills

- **add-wizard-handler** — Configure @WizardHandler (trigger, scope, state managers)
- **add-wizard-step** — Implement WizardStep (onEntry, validate, store, transitions)

## Reference

- [WizardHandler.kt](telegram-bot/src/commonMain/kotlin/eu/vendeli/tgbot/annotations/WizardHandler.kt)
- [WizardStep.kt](telegram-bot/src/commonMain/kotlin/eu/vendeli/tgbot/types/chain/WizardStep.kt)
- [TestWizard.kt](telegram-bot/src/jvmTest/kotlin/eu/vendeli/fixtures/TestWizard.kt)

---
> Converted and distributed by [TomeVault](https://tomevault.io/claim/vendelieu) — claim your Tome and manage your conversions.
<!-- tomevault:4.0:skill_md:2026-04-11 -->

