yaebal
type-safe · chainable · plugin-first

yaebal

Yet Another tElegram Bot Api Library.
the context type accumulates through the chain — handlers see what plugins added, with zero casting.

terminal
pnpm add yaebal
bot.ts
import { InlineKeyboard, callbackData, createBot, html, session } from "yaebal";

const vibe = callbackData("vibe", { score: Number });

const bot = createBot(process.env.BOT_TOKEN!)
  .install(session({ initial: () => ({ fire: 0 }) }))
  .derive((ctx) => ({ who: ctx.from?.first_name ?? "friend" }));

bot.command("start", (ctx) =>
  ctx.reply(html`<b>hey ${ctx.who}</b> — pick the vibe`, {
    reply_markup: new InlineKeyboard()
      .text("🔥 ship it", vibe.pack({ score: 1 }))
      .text("🚀 10x", vibe.pack({ score: 10 }))
      .build(),
  }),
);

bot.on("message:text", async (ctx) => {
  await ctx.react("🔥"); // generated MessageContext shortcut
  return ctx.reply(html`<code>${ctx.text}</code> landed in a typed handler`);
});

bot.callbackQuery(vibe.pattern, (ctx) => {
  const data = vibe.unpack(ctx.callbackQuery.data ?? "");
  ctx.session.fire += data?.score ?? 0;
  return ctx.answer(`vibe score: ${ctx.session.fire}`);
});

bot.start();
10.3 bot api, regenerated daily
0 runtime deps in core
50+ first-party packages

batteries, not a shopping list

retries, the callback-query spinner, message handles, the typing indicator, file downloads, 4096-char splitting — first-party, typed, and already in the yaebal package. every install widens the context type, so a missing one is a compile error.

preamble.ts
import { createBot, autoRetry, autoAnswer, hydrate, typing } from "yaebal";

// the six lines every production bot writes anyway — one import, no wiring
const bot = createBot(process.env.BOT_TOKEN!)
  .install(autoRetry())  // 429 flood-waits and transient 5xx retry themselves
  .install(autoAnswer()) // no callback query left spinning
  .install(hydrate())    // msg.editText(...) / msg.delete() on what you just sent
  .install(typing());    // ctx.typing(fn) holds "typing…" for as long as fn runs

bot.command("ask", (ctx) =>
  ctx.typing(async () => {
    const msg = ctx.hydrate(await ctx.reply("thinking…"));
    await msg.editText("here you go");
  }),
);

🛟 type-safe

strict mode, no any in public types. the context type flows through the whole chain.

⛓️ chainable

Bot extends Composer. derive / decorate / guard each return an augmented context type.

🚘 auto-gen contexts

every per-update context and its shortcut methods are generated from the Bot API schema.

🥇 plugin-first

a deep set of first-party plugins — filters, conversation, runner, and more. dependencies are explicit and type-checked, never implicit order.

MIT · built on the shoulders of grammY, GramIO & puregram