Download Latest Version 2.4.0 source code.zip (381.4 kB)
Home / v2.4.0
Name Modified Size InfoDownloads / Week
Parent folder
2.4.0 source code.tar.gz 2026-11-09 322.0 kB
2.4.0 source code.zip 2026-11-09 381.4 kB
README.md 2026-11-09 5.6 kB
Totals: 3 Items   708.9 kB 0

Bot API 10.4 (November 9, 2026)

Widgets

  • Added sendWidget, sendSharedWidget, answerWidgetQuery, editMessageWidgetData, and editSharedWidgetData.
  • Added Widget and widget on Message and ExternalReplyInfo.
  • Added WidgetQuery/SharedWidgetQuery and the widget_query/shared_widget_query update types, plus WidgetQueryType (WidgetQueryTypeData, WidgetQueryTypeSignData, WidgetQueryTypeSentTransaction) and WidgetQueryWallet.
  • Added InputWidgetMessageContent and InputSharedWidgetMessageContent.
  • ctx.chat/ctx.from resolve for widget queries (shared_widget_query has no chat).

    :::ts await bot.api.sendWidget({ chat_id: userId, // widgets work in private chats allow_queries: true, html: <button id="b">Tap me</button> <script> b.onclick = () => TelegramWidget.request("tap").then((a) => (b.textContent = a.result)); </script>, });

    bot.on("widget_query", async (ctx) => { if (!("widget_query" in ctx.update)) return; const q = ctx.update.widget_query; await ctx.api.answerWidgetQuery({ widget_query_id: q.id, data: hi ${ctx.from?.first_name} }); });

Ephemeral Messages

  • chat_id in the ephemeral-capable send methods and receiver_user_id in EphemeralMessageParameters are now optional, so callback_query_id alone sends an ephemeral reply to a callback query (in any chat except channel direct-messages chats).
  • Types are stricter than the "Optional" columns in the Bot API docs, to catch at compile time what Telegram rejects at runtime:
  • A call to one of these methods without chat_id is a type error unless it is an ephemeral reply to a callback query (callback_query_id without receiver_user_id). The rule is the exported ChatOrCallbackReply type.
  • EphemeralMessageParameters must have receiver_user_id or callback_query_id. The callback-reply form is the exported EphemeralCallbackReply type.
  • Omit and Pick merge the members of these param types, so a wrapper such as Omit<SendMessageParams, "parse_mode"> loses the rule and its spread is rejected. Use the exported DistributiveOmit<SendMessageParams, "parse_mode">. Omit<SendMessageParams, "chat_id"> plus your own chat_id works as before. interface X extends SendMessageParams no longer compiles; use a type intersection.
  • ctx.reply on an update without a chat (e.g. a callback query from an inline message) sends when other makes it an ephemeral reply to the callback query, instead of throwing.

    :::ts bot.on("callback_query", async (ctx) => { const q = ctx.callbackQuery; if (!q) return; // No chat_id: shown only to the user who tapped, marked "Only visible to you" await ctx.api.sendMessage({ text: "Only you can see this", ephemeral_message_parameters: { callback_query_id: q.id }, }); await ctx.answerCallbackQuery(); // still required after an ephemeral reply });

    await bot.api.sendMessage({ text: "hi" }); // ✗ type error: chat_id is required here

    // Wrapping the param types: use DistributiveOmit, not Omit import type { DistributiveOmit, SendMessageParams } from "node-telegram-bot-api";

    const sendHtml = (p: DistributiveOmit<SendMessageParams, "parse_mode">) => bot.api.sendMessage({ ...p, parse_mode: "HTML" });

Quotes from rich messages

  • Added rich_texts to TextQuote and quote_rich_texts to ReplyParameters.

    :::ts await bot.api.sendMessage({ chat_id: chatId, text: "Agreed!", reply_parameters: { message_id: richMessageId, quote_rich_texts: ["the exact sentence being quoted"], }, });

TON wallet

  • Added TonWalletCard, TonWalletCardSticker, TonNft, TonNftAttribute, and ChatFullInfo.ton_wallet_card.
  • Added TonWalletTransfer and Message.ton_wallet_transfer.
  • Renamed StoryAreaPosition to AreaPosition, following the Bot API. StoryAreaPosition stays as a deprecated alias.

Chat share themes

  • Added ChatShareTheme (ChatShareThemeEmoji, ChatShareThemeUniqueGift), ChatFullInfo.share_theme, and EmojiType (EmojiTypeEmoji, EmojiTypeCustomEmoji).
  • Added ChatShareThemeBackdrop, ChatShareThemeBackdrops, and getChatShareThemeBackdrops.

Gifts

  • Added getUniqueGift and deleteBusinessAccountGift.

General

  • Added RichTextTonAddress (with RichTextBuilder.tonAddress()) and the ton_address/bank_card_number entity types (with EntityType.TonAddress / EntityType.BankCardNumber). EntityType also gains the previously missing DateTime.
  • Added custom_description_parse_mode and custom_description_entities to verifyUser and verifyChat.
  • Added PaidMessagesWaived and Message.paid_messages_waived.

Rate limiting

  • rateLimit: { global, perChat } now takes the global and the per-chat token together, once both are available. Before, a request queued behind a busy chat spent its global token while it waited, so a chat backlog could later push sends over the global cap (#1390). Neither token is spent while a request waits on the other, and an aborted wait spends neither.
  • Added TokenBucket.takeAll(buckets, signal?), which takes one token from each bucket once all of them have one.

    :::ts // At most 30 requests/s overall and 1 request/s per chat, both checked at send time const bot = new Bot(TOKEN, { rateLimit: { global: 30, perChat: 1 } });

Full Changelog: https://github.com/yagop/node-telegram-bot-api/compare/v2.3.0...v2.4.0

Source: README.md, updated 2026-11-09