turbogram-v1
v1.0.3
Published
A blazing-fast, lightweight Telegram Bot SDK for Node.js with Lightspeed Engine™
Maintainers
Keywords
Readme
🚀 turbogram
The fastest Telegram Bot SDK for Node.js · Zero dependencies · Built for performance
turbogram-v1 is a blazing-fast, production-ready Telegram Bot framework powered by Lightspeed Engine™. It delivers maximum throughput with minimal overhead—no bloat, no unnecessary dependencies, just raw Node.js performance.
✨ Highlights
| Feature | Description | |---------|-------------| | ⚡ Lightspeed Engine™ | Microtask scheduling, update deduplication & memory pooling | | 📡 Broadcast System | Send messages to millions with concurrency control | | 🎭 Scene Manager | Complex multi-step bot flows | | 💬 Conversation Manager | Natural dialog flows with validation | | 💾 Session Manager | User state persistence (Memory/File) | | 🔄 Polling + Webhook | Flexible update ingestion | | 🎹 Keyboard Builder | Fluent API for inline & reply keyboards | | 🛡️ Telegram Verification | Built-in Web App & login validation | | 📝 Rich Formatting | Markdown, MarkdownV2 & HTML | | ⚙️ Middleware Engine | Composable, Express-style middleware | | 🎯 Zero Dependencies | 100% native Node.js APIs | | 📦 Ultra Lightweight | Minimal footprint, maximum speed |
📦 Installation
npm install turbogram-v1🚀 Quick Start
Polling Mode
import { Bot } from 'turbogram-v1';
const bot = new Bot({ token: 'YOUR_BOT_TOKEN' });
bot.command('/start', async (ctx) => {
await ctx.reply('Welcome!');
});
bot.startPolling();Webhook Mode
const bot = new Bot({ token: 'YOUR_BOT_TOKEN' });
bot.startWebhook({ port: 3000, url: 'https://domain.com/webhook' });⚠️ Important: Correct Usage Guide
✅ DO: Always use async/await
// ✅ CORRECT - Using async/await
bot.command('/start', async (ctx) => {
await ctx.reply('Hello World!');
});
bot.on('callback_query', async (ctx) => {
// Always answer callback first
await ctx.answerCallback();
// Then edit or reply
await ctx.edit('Updated text');
});❌ DON'T: Forget async/await
// ❌ WRONG - Missing async/await
bot.command('/start', (ctx) => {
ctx.reply('Hello World!'); // Promise not awaited!
});
bot.on('callback_query', (ctx) => {
ctx.edit('Updated text'); // Will fail silently!
});📚 Complete API Reference
Bot Methods
| Method | Description | Example |
|--------|-------------|---------|
| bot.command(command, handler) | Register command handler | bot.command('/start', async (ctx) => {}) |
| bot.hears(pattern, handler) | Listen for text patterns | bot.hears(/hello/i, async (ctx) => {}) |
| bot.on(event, handler) | Listen for events | bot.on('message', async (ctx) => {}) |
| bot.use(middleware) | Add middleware | bot.use(async (ctx, next) => {}) |
| bot.catch(handler) | Global error handler | bot.catch((error, ctx) => {}) |
| bot.startPolling() | Start long polling | await bot.startPolling() |
| bot.stopPolling() | Stop polling | bot.stopPolling() |
| bot.startWebhook(options) | Start webhook server | await bot.startWebhook({ port: 3000 }) |
| bot.broadcast(params) | Send mass messages | await bot.broadcast({ chatIds, text }) |
| bot.broadcastPhoto(params) | Broadcast photos | await bot.broadcastPhoto({ chatIds, photo }) |
| bot.verifyTelegramUser(data) | Verify Telegram user | bot.verifyTelegramUser(initData) |
Context Methods (ctx)
| Method | Description | Returns |
|--------|-------------|---------|
| ctx.reply(text, options?) | Send/reply text message | Promise<Message> |
| ctx.replyHTML(text, options?) | Send HTML formatted message | Promise<Message> |
| ctx.replyMarkdown(text, options?) | Send Markdown formatted | Promise<Message> |
| ctx.replyMarkdownV2(text, options?) | Send MarkdownV2 (auto-escaped) | Promise<Message> |
| ctx.replyPhoto(photo, options?) | Send photo | Promise<Message> |
| ctx.replyVideo(video, options?) | Send video | Promise<Message> |
| ctx.replyDocument(doc, options?) | Send document | Promise<Message> |
| ctx.replyAudio(audio, options?) | Send audio | Promise<Message> |
| ctx.replyVoice(voice, options?) | Send voice | Promise<Message> |
| ctx.replyAnimation(anim, options?) | Send animation | Promise<Message> |
| ctx.replySticker(sticker, options?) | Send sticker | Promise<Message> |
| ctx.replyLocation(lat, lon, options?) | Send location | Promise<Message> |
| ctx.replyVenue(lat, lon, title, address, options?) | Send venue | Promise<Message> |
| ctx.replyContact(phone, firstName, options?) | Send contact | Promise<Message> |
| ctx.replyPoll(question, options, options?) | Send poll | Promise<Message> |
| ctx.replyDice(emoji?, options?) | Send dice | Promise<Message> |
| ctx.edit(text, options?) | Edit current message | Promise<Message> |
| ctx.editMessageText(text, options?) | Edit message text (safe) | Promise<Message> |
| ctx.delete() | Delete current message | Promise<void> |
| ctx.answerCallback(text?, options?) | Answer callback query | Promise<void> |
| ctx.forwardMessage(chatId, options?) | Forward message | Promise<Message> |
| ctx.copyMessage(chatId, options?) | Copy message | Promise<Message> |
| ctx.pinMessage(options?) | Pin message | Promise<void> |
| ctx.unpinMessage(options?) | Unpin message | Promise<void> |
| ctx.enter(sceneName) | Enter a scene | Promise<void> |
| ctx.leave() | Leave current scene | Promise<void> |
| ctx.reenter() | Re-enter current scene | Promise<void> |
Context Properties
| Property | Type | Description |
|----------|------|-------------|
| ctx.update | Update | Raw Telegram update object |
| ctx.message | Message | Current message object |
| ctx.from | User | User who sent the message |
| ctx.chat | Chat | Current chat information |
| ctx.callbackQuery | CallbackQuery | Callback query data |
| ctx.session | Object | User session data (requires SessionManager) |
| ctx.scene | SceneManager | Scene manager instance |
| ctx.bot | Bot | Bot instance |
| ctx.api | TelegramAPI | Direct API access |
Events
| Event | Description |
|-------|-------------|
| message | New message received |
| text | Text message received |
| photo | Photo received |
| video | Video received |
| audio | Audio received |
| document | Document received |
| animation | Animation received |
| voice | Voice message received |
| sticker | Sticker received |
| contact | Contact shared |
| location | Location shared |
| venue | Venue shared |
| dice | Dice/sticker sent |
| poll | Poll received |
| poll_answer | Poll answer received |
| callback_query | Inline button clicked |
| inline_query | Inline query received |
| chosen_inline_result | Inline result selected |
| edited_message | Message edited |
| channel_post | Channel post received |
| edited_channel_post | Channel post edited |
| chat_member | Chat member updated |
| my_chat_member | Bot's chat member updated |
| chat_join_request | Chat join request |
Keyboard Builders
| Method | Description | Example |
|--------|-------------|---------|
| InlineKeyboard() | Create inline keyboard | InlineKeyboard().callback('Click', 'data') |
| ReplyKeyboard() | Create reply keyboard | ReplyKeyboard().text('Button') |
| RemoveKeyboard() | Remove keyboard | RemoveKeyboard() |
| ForceReplyMarkup() | Force reply | ForceReplyMarkup() |
Keyboard Builder Methods
| Method | Description | Keyboard Type |
|--------|-------------|---------------|
| .text(text) | Text button | Reply |
| .callback(text, data) | Callback button | Inline |
| .url(text, url) | URL button | Inline |
| .contactRequest(text) | Contact request | Reply |
| .locationRequest(text) | Location request | Reply |
| .pollRequest(text, type?) | Poll request | Reply |
| .webApp(text, url) | WebApp button | Both |
| .login(text, url, options?) | Login button | Inline |
| .pay(text) | Pay button | Inline |
| .switchInline(text, query) | Switch to inline | Inline |
| .switchInlineCurrent(text, query) | Switch inline current | Inline |
| .resize(enable) | Resize keyboard | Reply |
| .oneTime(enable) | One time keyboard | Reply |
| .persistent(enable) | Persistent keyboard | Reply |
| .selective(enable) | Selective keyboard | Reply |
| .placeholder(text) | Input placeholder | Reply |
| .row() | New row | Both |
| .build() | Build keyboard object | Both |
Reply Markup Options
// Inline Keyboard Markup
{
inline_keyboard: [
[
{ text: 'Button 1', callback_data: 'data1' },
{ text: 'Button 2', url: 'https://example.com' }
],
[
{ text: 'Button 3', callback_data: 'data3' }
]
]
}
// Reply Keyboard Markup
{
keyboard: [
[{ text: 'Button 1' }, { text: 'Button 2' }],
[{ text: 'Button 3' }]
],
resize_keyboard: true,
one_time_keyboard: false,
persistent: true
}
// Remove Keyboard
{
remove_keyboard: true,
selective: false
}
// Force Reply
{
force_reply: true,
selective: false,
input_field_placeholder: 'Type here...'
}Managers
| Class | Purpose | Methods |
|-------|---------|---------|
| SessionManager | User state persistence | get(), set(), delete(), save(), clear() |
| SceneManager | Multi-step bot flows | scene(), enter(), leave(), reenter(), middleware() |
| ConversationManager | Natural dialog flows | startConversation(), handleResponse(), middleware() |
Storage Options
| Class | Description | Use Case |
|-------|-------------|----------|
| MemoryStorage | In-memory storage | Development, small bots |
| FileStorage | File-based storage | Production, data persistence |
Utilities
import {
// Timing
sleep, // sleep(1000) - Wait for ms
debounce, // debounce(fn, 300) - Debounce function
throttle, // throttle(fn, 100) - Throttle function
// Formatting
escapeHTML, // escapeHTML('<b>') -> '<b>'
escapeMarkdown, // escapeMarkdown('*text*') -> '\*text\*'
escapeMarkdownV2,// escapeMarkdownV2('!text') -> '\!text'
// Data
randomId, // randomId(10) - Random ID
chunk, // chunk(array, 3) - Split array
deepMerge, // deepMerge(obj1, obj2) - Deep merge objects
// Files
downloadFile, // downloadFile(url, path) - Download file
getFileLink, // getFileLink(token, filePath) - Get file URL
// Commands
parseCommand, // parseCommand('/cmd arg1 arg2') - Parse command
// Verification
verifyHash, // verifyHash(data, token) - Verify hash
verifySignature, // verifySignature(initData, token) - Verify signature
} from 'turbogram-v1';🎹 Complete Keyboard Examples
Inline Keyboard with Callbacks
import { InlineKeyboard } from 'turbogram-v1';
bot.command('/menu', async (ctx) => {
const keyboard = InlineKeyboard()
.callback('✅ Approve', 'approve')
.callback('❌ Reject', 'reject')
.row()
.url('🌐 Website', 'https://example.com')
.switchInline('📤 Share', 'check this')
.row()
.callback('🔙 Back', 'back')
.build();
await ctx.reply('Choose an action:', { reply_markup: keyboard });
});
// Handle callbacks correctly
bot.on('callback_query', async (ctx) => {
const data = ctx.callbackQuery?.data;
// IMPORTANT: Always answer callback first!
await ctx.answerCallback();
// Then handle the action
switch (data) {
case 'approve':
await ctx.edit('✅ Approved!');
break;
case 'reject':
await ctx.edit('❌ Rejected!');
break;
case 'back':
await ctx.edit('🔙 Back to main menu');
break;
}
});Reply Keyboard with Options
import { ReplyKeyboard, RemoveKeyboard } from 'turbogram-v1';
bot.command('/contact', async (ctx) => {
const keyboard = ReplyKeyboard()
.resize(true)
.oneTime(true)
.placeholder('Share your info...')
.contactRequest('📱 Share Contact')
.row()
.locationRequest('📍 Share Location')
.row()
.text('❌ Cancel')
.build();
await ctx.reply('Please share your information:', {
reply_markup: keyboard
});
});
bot.hears('❌ Cancel', async (ctx) => {
await ctx.reply('Keyboard removed. Use /start to return.', {
reply_markup: RemoveKeyboard()
});
});🔄 Callback Query Best Practices
✅ Correct Pattern
// Always follow this pattern for callbacks
bot.on('callback_query', async (ctx) => {
const data = ctx.callbackQuery?.data;
const userId = ctx.from?.id;
// Step 1: Answer callback immediately
await ctx.answerCallback();
// Step 2: Try to edit the message
try {
await ctx.edit(`You selected: ${data}`, {
reply_markup: newKeyboard
});
} catch (error) {
// Step 3: If edit fails, send new message
console.log('Edit failed, sending new message');
await ctx.reply(`You selected: ${data}`, {
reply_markup: newKeyboard
});
}
});❌ Wrong Pattern (Will Cause Errors)
// DON'T DO THIS!
bot.on('callback_query', (ctx) => {
const data = ctx.callbackQuery?.data;
// Missing async/await
// Missing answerCallback first
// Missing error handling
ctx.edit(`You selected: ${data}`); // ❌ Will fail!
});📝 Message Editing Guide
edit() vs editMessageText()
// ctx.edit() - Automatically uses current message ID
await ctx.edit('Updated text', {
parse_mode: 'HTML',
reply_markup: keyboard
});
// ctx.editMessageText() - Same but more explicit
await ctx.editMessageText('Updated text', {
chat_id: chatId,
message_id: messageId,
parse_mode: 'HTML',
reply_markup: keyboard
});When Edit Fails
// Safe edit function
async function safeEdit(ctx, text, options = {}) {
try {
// Check if text is not empty
if (!text || text.trim() === '') {
throw new Error('Text cannot be empty');
}
// Try to edit
await ctx.edit(text, options);
return true;
} catch (error) {
// If edit fails, send new message
console.log('Edit failed:', error.message);
await ctx.reply(text, options);
return false;
}
}
// Usage
bot.on('callback_query', async (ctx) => {
await ctx.answerCallback();
await safeEdit(ctx, '✅ Action completed!', {
reply_markup: getMainMenuKeyboard()
});
});💾 Session Manager
Store user data across messages:
import { SessionManager, sessionMiddleware, MemoryStorage } from 'turbogram-v1';
const sessions = new SessionManager({
ttl: 3600, // 1 hour
storage: new MemoryStorage(),
});
bot.use(sessionMiddleware(sessions));
// Now ctx.session is available
bot.on('text', async (ctx) => {
ctx.session.messageCount = (ctx.session.messageCount || 0) + 1;
ctx.session.lastMessage = ctx.message?.text;
await ctx.reply(`Messages sent: ${ctx.session.messageCount}`);
});🎭 Scene Manager
Build complex multi-step flows:
import { SceneManager } from 'turbogram-v1';
const scenes = new SceneManager();
scenes.scene('order')
.enter('order', async (ctx) => {
await ctx.reply('What would you like to order?');
})
.hear('order', /pizza|burger/i, async (ctx) => {
ctx.session.order = ctx.message?.text;
await ctx.reply('What size? (Small/Medium/Large)');
})
.hear('order', /small|medium|large/i, async (ctx) => {
ctx.session.size = ctx.message?.text;
await ctx.reply(`✅ Ordered: ${ctx.session.size} ${ctx.session.order}!`);
await ctx.leave();
});
scenes.default('main');
bot.use(scenes.middleware());
bot.command('/order', async (ctx) => {
await ctx.enter('order');
});💬 Conversation Manager
Natural dialog flows with validation:
import { ConversationManager } from 'turbogram-v1';
const conversations = new ConversationManager();
bot.use(conversations.middleware());
bot.command('/register', async (ctx) => {
await conversations.startConversation(ctx, {
id: 'registration',
steps: [
{
question: 'What is your name?',
validate: (answer) => answer.length >= 2,
errorMessage: 'Name must be at least 2 characters.',
},
{
question: 'How old are you?',
validate: (answer) => {
const age = parseInt(answer);
return age >= 13 && age <= 120;
},
errorMessage: 'Please enter a valid age (13-120).',
transform: (answer) => parseInt(answer),
},
],
onComplete: async (answers, ctx) => {
await ctx.replyHTML(`
<b>✅ Registration Complete!</b>
<b>Name:</b> ${answers.step_0}
<b>Age:</b> ${answers.step_1}
`);
},
onCancel: async (ctx) => {
await ctx.reply('❌ Registration cancelled.');
},
timeout: 300000,
});
});📡 Broadcast System
await bot.broadcast({
chatIds: [123, 456, 789],
text: '📢 Important Announcement',
options: {
concurrency: 5,
delay: 50,
retry: 3,
ignoreErrors: true,
onProgress: (sent, total) => {
console.log(`Progress: ${sent}/${total}`);
},
onComplete: (results) => {
console.log(`✅ Sent ${results.length} messages`);
},
onError: (error, chatId) => {
console.error(`Failed for ${chatId}:`, error.message);
}
}
});⚡ Lightspeed Engine™ Architecture
| Component | Benefit | |-----------|---------| | Microtask Scheduler | Sub-millisecond update dispatch | | Update Deduplication | Prevents redundant processing | | Memory Pooling | Reduces GC pressure under load | | Keep-Alive Connections | HTTP agent reuse for faster requests | | JSON Parse Cache | Avoids re-parsing identical payloads | | Batch Processing | Processes 50 updates per microtask | | Lazy Context Creation | Minimal object allocation |
🎯 Complete Production Example
import {
Bot, SessionManager, SceneManager, ConversationManager,
sessionMiddleware, InlineKeyboard, ReplyKeyboard,
MemoryStorage
} from 'turbogram-v1';
const bot = new Bot({ token: process.env.BOT_TOKEN });
// Setup managers
const sessions = new SessionManager({ storage: new MemoryStorage() });
const scenes = new SceneManager();
const conversations = new ConversationManager();
// Setup middleware pipeline
bot.use(sessionMiddleware(sessions));
bot.use(scenes.middleware());
bot.use(conversations.middleware());
// Request logger middleware
bot.use(async (ctx, next) => {
const start = Date.now();
await next();
console.log(`Request: ${Date.now() - start}ms`);
});
// Main scene
scenes.scene('main').default('main')
.enter('main', async (ctx) => {
const keyboard = ReplyKeyboard()
.resize(true)
.text('🛒 Shop')
.text('👤 Profile')
.build();
await ctx.reply('Main Menu:', { reply_markup: keyboard });
});
// Shop scene
scenes.scene('shop')
.enter('shop', async (ctx) => {
const keyboard = InlineKeyboard()
.callback('📦 Product 1', 'buy_1')
.callback('📦 Product 2', 'buy_2')
.build();
await ctx.reply('Products:', { reply_markup: keyboard });
});
// Commands
bot.command('/start', async (ctx) => {
ctx.session.visits = (ctx.session.visits || 0) + 1;
await ctx.enter('main');
});
// Text handlers
bot.hears('🛒 Shop', async (ctx) => {
await ctx.enter('shop');
});
// Callback handler (CORRECT PATTERN)
bot.on('callback_query', async (ctx) => {
await ctx.answerCallback();
try {
await ctx.edit('✅ Added to cart!');
} catch (error) {
await ctx.reply('✅ Added to cart!');
}
});
// Error handler
bot.catch(async (error, ctx) => {
console.error('Error:', error.message);
try {
if (ctx?.callbackQuery) {
await ctx.answerCallback('⚠️ Error occurred');
} else if (ctx?.message) {
await ctx.reply('⚠️ An error occurred. Please try again.');
}
} catch (e) {
console.error('Failed to send error message:', e.message);
}
});
// Start bot
bot.startPolling().then(() => {
console.log('✅ Bot is running!');
}).catch(console.error);
// Graceful shutdown
process.once('SIGINT', () => {
console.log('Shutting down...');
bot.stopPolling();
process.exit(0);
});📊 Performance Benchmarks
| Metric | Value | |--------|-------| | Update Processing | < 1ms per update | | Memory Usage | < 10MB base | | Middleware Overhead | < 0.1ms | | Concurrent Users | 10,000+ | | Dependencies | 0 |
📄 License
MIT © TurboGram
Built for speed. Designed for scale.
🤝 Contributing
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Submit a Pull Request
For major changes, please open an issue first to discuss your ideas.
