message extras
telegram messages have many optional controls: replies, link previews, notifications, protection, reactions, captions, entities, keyboards, and effects.
reply parameters
ctx.reply() fills reply parameters for the current message. use raw params when you
need custom threading behavior.
import { createBot } from "yaebal";
const bot = createBot(process.env.BOT_TOKEN!);
bot.on("message:text", async (ctx) => {
await ctx.reply("threaded reply");
await ctx.send("manual reply", {
reply_parameters: { message_id: ctx.message!.message_id },
});
});ctx.quote() is sugar for the common "reply quoting part of the message" case:
import { createBot } from "yaebal";
const bot = createBot(process.env.BOT_TOKEN!);
// quote() is sugar for reply_parameters.quote — replies pinned to a specific
// substring of the original message instead of the whole thing.
bot.on("message:text", (ctx) => ctx.quote(ctx.text.slice(0, 20), "about that part —"));link previews
import { createBot } from "yaebal";
const bot = createBot(process.env.BOT_TOKEN!);
bot.command("share", (ctx) =>
ctx.reply("https://yaebal.mom", {
link_preview_options: {
is_disabled: false,
prefer_large_media: true,
},
}),
);reactions
generated contexts expose convenience shortcuts like ctx.react() when you use createBot() from the meta package — it accepts a bare emoji, an emoji plus a custom
emoji id, or no arguments at all to clear the bot's own reaction.
import { createBot } from "yaebal";
const bot = createBot(process.env.BOT_TOKEN!);
bot.on("message:text", async (ctx) => {
await ctx.react("🔥"); // one emoji
await ctx.react("🔥", "12345"); // emoji + a custom emoji id
await ctx.react(); // no args — clears this bot's reaction
});other send options
import { createBot, InlineKeyboard } from "yaebal";
const bot = createBot(process.env.BOT_TOKEN!);
bot.command("pick", (ctx) =>
ctx.reply("pick", {
protect_content: true,
disable_notification: true,
reply_markup: new InlineKeyboard().text("ok", "ok"),
}),
);captions and entities
a caption takes the same FormatResult a message body does — @yaebal/fmt's html/md or
core's entity builders split into caption/caption_entities automatically, the same splitting ctx.send() does for plain text.
import { createBot, html, media } from "yaebal";
const bot = createBot(process.env.BOT_TOKEN!);
bot.command("pic", (ctx) =>
// caption accepts the same FormatResult as text — html/md/format results are
// split into caption + caption_entities automatically, same as ctx.send().
ctx.sendPhoto(media.url("https://example.com/cat.jpg"), {
caption: html`<b>cute cat</b>, no filter`,
}),
);message effects
message_effect_id plays a full-screen animation on private chats (🎉, 🔥, ❤️, 👍, 👎,
💩 as of this writing) — send-only, private chats only, and only from a real params object (not
available on every context shortcut's positional-string sugar).
import { createBot } from "yaebal";
const bot = createBot(process.env.BOT_TOKEN!);
// one of Telegram's small fixed set of animated effect ids (🔥 shown by clients
// as a full-screen animation) — there's no yaebal-side enum, it's a raw id
// telegram assigns; capture the ones you use as named constants in your own code.
const FIRE_EFFECT_ID = "5104841245755180586";
bot.command("celebrate", (ctx) => ctx.reply("🎉", { message_effect_id: FIRE_EFFECT_ID }));formatting
prefer entity-based formatting through @yaebal/fmt or core builders. it avoids broken markdown/html escaping and makes user interpolation safe.