Chisle
The coding-agent plugin that bills 52% of a bare model across 20 live tasks. Terse persona + tool-output compressor + context diet.
翻訳待ち
Telegram Bot API for NodeJS
翻訳待ち
開発ツールTypeScript · JavaScriptGitHub @yagop自分でインストール所有者未確認
Paste into Claude or ChatGPT for installation instructions suited to your environment.
Have you tried it? One tap is enough. No sign-in needed.
Loading the full README. The saved excerpt is shown below.
✨ A Modern Telegram Bot API Library ✨ v2 is a from-scratch redesign, no v1 compatibility. Coming from v1? See the v1 -> v2 migration guide in the changelog. **Runs on Bun, modern Node.js, Deno, Cloudflare Workers and Vercel Functions λ Api mirrors the wire API 1:1 - one method per Bot API method, each taking a single params object. koa-style middleware around every update; on/command/hears are filters in the same chain. Wrap downstream work with await next().Read the full README ↗
v2 is a from-scratch redesign, no v1 compatibility. Coming from v1? See the v1 -> v2 migration guide in the changelog.
npm install node-telegram-bot-api
**Runs on Bun, modern Node.js, Deno, Cloudflare Workers and Vercel Functions λ
import { Bot, InlineKeyboardBuilder } from "node-telegram-bot-api";
import { run } from "node-telegram-bot-api/node"; // managed runner: wires Ctrl-C to bot.stop()
const bot = new Bot(process.env.BOT_TOKEN!);
// commands, regex and update types are all middleware - registration order wins
bot.command("start", (ctx) => ctx.reply("Hi! Send me anything."));
bot.hears(/echo (.+)/, (ctx) => ctx.reply(ctx.match![1]!));
bot.on("message", (ctx) =>
ctx.reply("Pick one:", {
reply_markup: new InlineKeyboardBuilder()
.text("👍", "up")
.text("👎", "down")
.build(),
}),
);
// 🔘 a tapped inline button comes back as a callback_query
bot.on("callback_query", async (ctx) => {
await ctx.answerCallbackQuery({ text: `You tapped ${ctx.callbackQuery!.data}` });
});
await run(bot); // core-only alternative that runs anywhere: await bot.startPolling()
Api mirrors the wire API 1:1 - one method per Bot API method, each taking a single params object.
import { Api } from "node-telegram-bot-api";
const api = new Api(process.env.BOT_TOKEN!);
const me = await api.getMe();
await api.sendMessage({ chat_id: 12345, text: "hello" });
// the same client is also on bot.api and ctx.api
koa-style middleware around every update; on/command/hears are filters in the same chain. Wrap downstream work with await next().
// ⏱️ time every update - and catch anything thrown downstream
bot.use(async (ctx, next) => {
const start = Date.now();
try {
await next();
} finally {
console.log(`update took ${Date.now() - start}ms`);
}
});
// 🧯 last-resort error handler
bot.catch((err, ctx) => console.error("handler failed", err));
Structured fields are plain typed objects - pass a literal or use a fluent builder; the pipeline serializes either.
import { Bot, InlineKeyboardBuilder, ReplyKeyboardBuilder, EntityBuilder } from "node-telegram-bot-api";
const bot = new Bot(process.env.BOT_TOKEN!);
// 🎛️ inline keyboard as reply_markup
await bot.api.sendMessage({
chat_id,
text: "Choose:",
reply_markup: new InlineKeyboardBuilder()
.text("A", "a")
.url("Docs", "https://core.telegram.org/bots/api")
.row()
.text("B", "b")
.build(),
});
// ⌨️ reply keyboard as reply_markup
await bot.api.sendMessage({
chat_id,
text: "Yes or no?",
reply_markup: new ReplyKeyboardBuilder()
.text("Yes")
.text("No")
.build({ resize_keyboard: true }),
});
// ✍️ rich text - EntityBuilder computes UTF-16 offsets for you
const { text, entities } = new EntityBuilder()
.plain("Hello ")
.bold("world")
.link("docs", "https://github.com/yagop/node-telegram-bot-api")
.build();
await bot.api.sendMessage({ chat_id, text, entities });
// any structured field is just a plain object - no wrapper needed
await bot.api.sendMessage({ chat_id, text: "hi", link_preview_options: { is_disabled: true } });
A bare string is always a file_id or URL. Wrap raw bytes to upload them. Pass a
filename with the right extension - the core does no content sniffing, so the name is
what Telegram sees (fromPath uses the basename).
Uploads stream: bytes flow from their source straight into the request, so memory stays
flat no matter the file size (fromPath re-opens a disk stream per attempt). A Blob
or Uint8Array upload is re-streamed if the transport retries; a ReadableStream is
one-shot - it is sent once and a failure surfaces immediately instead of retrying. To
keep retries for an arbitrary stream source, pass a factory that returns a fresh stream:
// replayable streaming upload: the factory opens a new stream per attempt
await bot.api.sendVideo({
chat_id,
video: new InputFile(() => openVideoStream(), { filename: "video.mp4", contentType: "video/mp4" }),
});
import { Bot, InputFile, MediaGroupBuilder } from "node-telegram-bot-api";
import { fromPath } from "node-telegram-bot-api/node";
const bot = new Bot(process.env.BOT_TOKEN!);
// upload from disk (Node only)
await bot.api.sendPhoto({ chat_id, photo: await fromPath("./cat.jpg") });
// upload raw bytes (web-standard, runs anywhere)
await bot.api.sendDocument({ chat_id, document: new InputFile(bytes, { filename: "report.pdf" }) });
// a raw InputFile nested in a structure is auto-hoisted to an attach:// part
await bot.api.sendMediaGroup({
chat_id,
media: [
{ type: "photo", media: new InputFile(bytesA, { filename: "a.jpg" }), caption: "A" },
{ type: "photo", media: "https://telegram.org/example/photo.jpg" },
],
});
// MediaGroupBuilder: optional sugar for the same array
await bot.api.sendMediaGroup({
chat_id,
media: new MediaGroupBuilder()
.photo({ media: new InputFile(bytesA, { filename: "a.jpg" }), caption: "A" })
.photo({ media: "https://telegram.org/example/photo.jpg" })
.build(),
});
This is the beginning of the README. Read the rest at the original. Read the full README ↗