migrate from gramio

gramio and yaebal share the chainable composer idea, so migration is mostly naming, package selection, and runtime context wiring.

mental model

gramioyaebal
Bot is a chainable composerBot 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 packagecore entity builders plus @yaebal/fmt for html/markdown parsing

initialization

init.ts
// 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.

handlers.ts
// 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.

context.ts
// 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

plugins.ts
// gramio
bot.extend(session());

// yaebal
bot.install(session({ initial: () => ({}) }));

install order is type-checked. if a plugin requires ctx.session, install session first.

formatting

formatting.ts
// 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.

keyboard.ts
// gramio
new InlineKeyboard().text("yes", "yes");

// yaebal
new InlineKeyboard().text("yes", "yes");

webhooks

webhook.ts
// yaebal fetch webhook
import { webhook } from "yaebal";

export default {
  fetch: webhook(bot, { secretToken: env.SECRET }),
};

migration checklist

  • replace app imports with yaebal or 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.