migrate from gramio
gramio and yaebal share the chainable composer idea, so migration is mostly naming, package selection, and runtime context wiring.
mental model
| gramio | yaebal |
|---|---|
Bot is a chainable composer | Bot still extends Composer |
.extend(plugin) | .install(plugin) for plugins, .extend(composer) for composers |
ctx.send() | ctx.send() and ctx.reply(); prefer reply when responding to a message |
| format package | core entity builders plus @yaebal/fmt for html/markdown parsing |
initialization
// gramio
import { Bot } from "gramio";
const bot = new Bot(process.env.BOT_TOKEN as string);
// yaebal
import { createBot } from "yaebal";
const bot = createBot(process.env.BOT_TOKEN!);handlers
yaebal uses filter queries like message:text to narrow the context type. when a handler
needs text, prefer that over a generic message route.
// gramio
bot.command("start", (ctx) => ctx.send("hello"));
bot.on("message", (ctx) => ctx.send(ctx.text ?? ""));
bot.callbackQuery("ok", (ctx) => ctx.answer());
// yaebal
bot.command("start", (ctx) => ctx.reply("hello"));
bot.on("message:text", (ctx) => ctx.reply(ctx.text));
bot.callbackQuery("ok", (ctx) => ctx.answerCallbackQuery());derive and decorate
this part maps almost directly. keep async, per-update work in derive and static
services in decorate.
// gramio
const bot = new Bot(token)
.derive(async (ctx) => ({ user: await loadUser(ctx.from!.id) }))
.decorate({ version: "1.0.0" });
// yaebal
const bot = createBot(token)
.derive(async (ctx) => ({ user: await loadUser(ctx.from!.id) }))
.decorate({ version: "1.0.0" });plugins
// gramio
bot.extend(session());
// yaebal
bot.install(session({ initial: () => ({}) }));install order is type-checked. if a plugin requires ctx.session, install session first.
formatting
// gramio
ctx.send(format`${bold("hello")}`);
// yaebal
ctx.send(format`${bold("hello")}`);
ctx.send(html`<b>hello</b>`);keyboards
the fluent keyboard shape is intentionally familiar. yaebal also adds typed callback-data helpers.
// gramio
new InlineKeyboard().text("yes", "yes");
// yaebal
new InlineKeyboard().text("yes", "yes");webhooks
// yaebal fetch webhook
import { webhook } from "yaebal";
export default {
fetch: webhook(bot, { secretToken: env.SECRET }),
};migration checklist
- replace app imports with
yaebalor granular@yaebal/*packages. - change plugin registration to
.install(plugin()). - replace generic text handlers with
on("message:text"). - use
createBot()when you want generated context shortcuts at runtime. - move tests to @yaebal/test actor flows.