@yaebal/onboarding
declarative first-run tutorials and product tours. build a flow, install it as a typed plugin,
then control it from handlers through ctx.onboarding.<id>.
install
pnpm add @yaebal/onboardingusage
createOnboarding({ id }) returns a fluent builder. each step() adds a typed step id; after .build(), bot.install(welcome) widens the
context with ctx.onboarding.welcome.
import { Bot } from "@yaebal/core";
import { createOnboarding } from "@yaebal/onboarding";
const welcome = createOnboarding({ id: "welcome" })
.step("hello", {
text: "hi. i'll show you around.",
buttons: ["next", "dismiss"],
})
.step("commands", {
text: "use /help for commands and /settings to tune the bot.",
buttons: ["next", "exit"],
})
.step("done", { text: "you're ready." })
.onComplete((ctx) => ctx.send("welcome aboard."))
.build();
const bot = new Bot(token).install(welcome);
bot.command("start", (ctx) => ctx.onboarding.welcome.start());
bot.command("tour", (ctx) => ctx.onboarding.welcome.start({ force: true }));flow controls
bot.command("status", (ctx) => {
const flow = ctx.onboarding.welcome;
return ctx.reply("status=" + flow.status + ", step=" + (flow.currentStep ?? "none"));
});
bot.command("skip", (ctx) => ctx.onboarding.welcome.skip());
bot.command("exit", (ctx) => ctx.onboarding.welcome.exit());
bot.command("disable", (ctx) => ctx.onboarding.disableAll());
bot.command("enable", (ctx) => ctx.onboarding.enableAll());| member | description |
|---|---|
status | null | active | paused | exited | completed | dismissed |
currentStep | the active step id, typed from the builder chain |
data | mutable JSON-ish bag persisted with the flow record |
start(opts?) | start or resume. pass force: true to restart after completion |
next({ from }) | advance with an optional stale-step guard |
goto(id) | jump to a typed step id |
skip/exit/dismiss/undismiss/complete | terminal and convenience operations |
buttons
built-in buttons generate safe onboarding callback tokens. explicit button objects can jump to a
step, link out, or use your own callback_data.
.step("pick", {
text: "where next?",
buttons: [
"next",
{ text: "skip setup", goto: "done" },
{ text: "docs", url: "https://yaebal.pages.dev" },
],
})storage & scope
createOnboarding({
id: "welcome",
storage: myStorage, // { get(key), set(key, value), delete(key) }
scope: "user", // default. use "chat" for per-chat tours
});state is in memory by default. scope: "user" keys by ctx.from.id; scope: "chat" keys by ctx.chat.id; a function can return a custom key.
example bot
there is a runnable bot under examples/onboarding.
run it with pnpm --filter @yaebal/example-onboarding dev after adding BOT_TOKEN to its .env file.
_, and -. last step completes after render. if a flow has no next step, onboarding renders the current step, marks the flow
completed, then runs onComplete.