telegrams-node-bot
v1.0.1
Published
Ultra-lightweight, high-performance Telegram Bot framework for low-resource environments
Maintainers
Keywords
Readme
telegrams-node-bot 🚀
Ultra-lightweight, high-performance Telegram Bot Framework for Node.js
telegrams-node-bot is a minimalist Telegram bot framework designed specifically for low-resource environments. It delivers powerful features while consuming minimal RAM and CPU resources.
📚 Table of Contents
- Why telegrams-node-bot?
- Features
- Installation
- Quick Start
- Core Concepts
- API Reference
- Advanced Usage
- Performance Optimization
- Security Best Practices
- TypeScript Support
- Examples
- Contributing
- License
🌟 Why telegrams-node-bot?
Most Telegram bot frameworks are heavy, consuming hundreds of megabytes of RAM and significant CPU resources. telegrams-node-bot is different.
Performance Comparison
| Metric | Traditional Frameworks | telegrams-node-bot | |--------|----------------------|-----------| | RAM Usage | 100-300 MB | 10-30 MB | | Startup Time | 2-5 seconds | < 100ms | | Dependencies | 10-50 packages | 0 dependencies | | Command Lookup | O(n) linear scan | O(1) Map-based | | CPU Usage | High (event loops) | Minimal |
Key Benefits
- ✅ Zero dependencies — Uses only Node.js built-in APIs
- ✅ Memory efficient — Bounded queues and automatic cleanup
- ✅ Fast execution — Map-based O(1) command resolution
- ✅ TypeScript native — Full type support out of the box
- ✅ Production ready — Built-in error handling and rate limiting
- ✅ Mini App support — Secure validation and authentication
- ✅ Session management — Built-in per-user state tracking
- ✅ Middleware system — Composable request pipeline
✨ Features
Core Features
- Lightweight Architecture — Optimized for low-end VPS and shared hosting
- Fast Command System — O(1) Map-based command lookup
- Smart Rate Limiting — Protect against spam and abuse
- Request Queue — Bounded concurrency for API calls
- Inline Keyboards — Full support with callbacks
- Reply Keyboards — Custom keyboards with resize options
- Message Editing — Edit messages and reply markups
- Payment Support — Telegram Stars and invoices
- Mini App Integration — Secure initData validation
- Webhook Support — Both polling and webhook modes
- Session Store — Built-in per-user state management with TTL
- Middleware Pipeline — Compose reusable request handlers
Advanced Features
- Inline Queries — Handle inline mode requests
- File Uploads — Send photos, documents, and media
- Chat Actions — Typing indicators and status updates
- Priority Queue — Prioritize important API calls
- Error Recovery — Graceful handling of network issues
- Rate Limit Info — Check remaining requests and reset time
📦 Installation
Prerequisites
- Node.js 18 or higher
- npm, yarn, or pnpm
Install
# Using npm
npm install telegrams-node-bot
# Using yarn
yarn add telegrams-node-bot
# Using pnpm
pnpm add telegrams-node-bot🚀 Quick Start
1. Get Your Bot Token
- Open Telegram and search for @BotFather
- Send
/newbotcommand - Follow the instructions
- Copy your bot token
2. Create Your First Bot
JavaScript (ESM) — bot.js:
import { LiteGram, keyboard, inlineKeyboard } from "telegrams-node-bot";
// Initialize bot
const bot = new LiteGram({
token: "YOUR_BOT_TOKEN",
polling: true // Auto-start polling
});
// Handle /start command
bot.command("start", async (ctx) => {
const name = ctx.from?.first_name || "User";
await ctx.reply(`Hello ${name}! 👋`, {
reply_markup: keyboard([
["📝 Register", "ℹ️ Info"],
["💰 Premium"]
])
});
});
// Handle /menu command
bot.command("menu", async (ctx) => {
await ctx.reply("Choose an option:", {
reply_markup: inlineKeyboard([
[
{ text: "Button 1", callback_data: "btn1" },
{ text: "Button 2", callback_data: "btn2" }
],
[
{ text: "Open Website", url: "https://example.com" }
]
])
});
});
// Start bot
bot.start();
console.log("Bot is running...");TypeScript — bot.ts:
import { LiteGram, keyboard, inlineKeyboard, Context } from "telegrams-node-bot";
const bot = new LiteGram({
token: "YOUR_BOT_TOKEN",
polling: true
});
bot.command("start", async (ctx: Context) => {
await ctx.reply("Welcome to telegrams-node-bot!");
});
bot.start();3. Run Your Bot
# JavaScript
node bot.js
# TypeScript
npx tsc && node dist/bot.js🎯 Core Concepts
1. Bot Instance
The LiteGram class is the main entry point:
const bot = new LiteGram({
token: "YOUR_BOT_TOKEN", // Required: Bot token from BotFather
polling: true, // Auto-start polling
timeout: 30, // Polling timeout in seconds
limit: 100, // Updates per request
apiRoot: "https://api.telegram.org", // Custom API endpoint
testMode: false // Test mode for payments
});Configuration Options:
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| token | string | — | Required. Bot token from BotFather |
| polling | boolean | false | Auto-start long polling on init |
| timeout | number | 30 | Polling timeout in seconds |
| limit | number | 100 | Max updates per request |
| allowed_updates | string[] | all | Update types to receive |
| apiRoot | string | https://api.telegram.org | Custom API endpoint |
| testMode | boolean | false | Test mode for payments |
2. Context Object
Every handler receives a ctx (context) object:
bot.command("start", async (ctx) => {
// User information
console.log(ctx.from); // TelegramUser object
console.log(ctx.chat); // Chat object
console.log(ctx.message); // Message object
// Response methods
await ctx.reply("Hello!"); // Send message
await ctx.replyWithPhoto("https://example.com/photo.jpg");
await ctx.sendChatAction("typing");
});Context Properties:
| Property | Type | Description |
|----------|------|-------------|
| ctx.update | Update | Raw update object |
| ctx.from | TelegramUser | User who sent the message |
| ctx.chat | TelegramChat | Chat information |
| ctx.message | TelegramMessage | Message object |
| ctx.callbackQuery | CallbackQuery | Callback query data |
| ctx.preCheckoutQuery | PreCheckoutQuery | Payment pre-checkout data |
| ctx.successfulPayment | SuccessfulPayment | Successful payment data |
| ctx.session | SessionContext | Per-user session storage |
Context Methods:
| Method | Description |
|--------|-------------|
| ctx.reply(text, options) | Send a message |
| ctx.replyWithPhoto(photo, options) | Send a photo |
| ctx.editMessageText(options) | Edit message text |
| ctx.editMessageReplyMarkup(markup) | Edit inline keyboard |
| ctx.deleteMessage() | Delete the current message |
| ctx.answerCallbackQuery(text, showAlert) | Answer callback query |
| ctx.sendInvoice(options) | Send payment invoice |
| ctx.sendStarsInvoice(options) | Send Stars invoice |
| ctx.sendChatAction(action) | Send typing/upload action |
| ctx.getChat() | Get chat information |
3. Command System
Commands are resolved in O(1) time using Map-based lookup:
// Register command handler
bot.command("start", async (ctx) => {
await ctx.reply("Starting...");
});
// Multiple commands
bot.command("help", handleHelp);
bot.command("info", handleInfo);
bot.command("settings", handleSettings);How it works:
- Commands are stored in a
Mapkeyed by command name - Lookup is O(1) instead of O(n) linear scan
- Command names are case-insensitive and lowercased automatically
4. Event Handlers
Handle different types of updates:
// Message handler (all text messages)
bot.on("message", async (ctx) => {
console.log(ctx.message?.text);
});
// Callback query handler
bot.on("callback_query", async (ctx) => {
const data = ctx.callbackQuery?.data;
await ctx.answerCallbackQuery(`You clicked: ${data}`);
});
// Inline query handler
bot.on("inline_query", async (ctx) => {
// Handle inline queries
});Supported Events:
| Event | Description |
|-------|-------------|
| message | Text messages and media |
| callback_query | Inline button clicks |
| inline_query | Inline mode queries |
| edited_message | Edited messages |
| channel_post | Channel posts |
5. Session Management
telegrams-node-bot includes a built-in Session Store for per-user state:
bot.command("register", async (ctx) => {
// Save session state
ctx.session.set({ step: "waiting_name" });
await ctx.reply("What's your name?");
});
bot.on("message", async (ctx) => {
const session = ctx.session.get();
if (session?.step === "waiting_name") {
// Update session state
ctx.session.update({
name: ctx.message?.text,
step: "waiting_age"
});
await ctx.reply(`Hi ${ctx.message?.text}! How old are you?`);
} else if (session?.step === "waiting_age") {
await ctx.reply(`Registered! Name: ${session.name}, Age: ${ctx.message?.text}`);
// Clear session
ctx.session.delete();
}
});Session Methods:
| Method | Description |
|--------|-------------|
| ctx.session.get<T>() | Get session data |
| ctx.session.set(value, ttl?) | Set session data (with optional TTL in seconds) |
| ctx.session.update(partial) | Merge partial data into session |
| ctx.session.delete() | Delete session for current user |
| ctx.session.clear() | Alias for delete() |
Features:
- Per-user and per-chat isolation
- Optional TTL (auto-expiry in seconds)
- Automatic cleanup of expired entries
- In-memory storage (fast, no dependencies)
6. Middleware System
Compose reusable logic that runs before every update:
// Logging middleware
bot.use(async (ctx, next) => {
console.log(`[${new Date().toISOString()}] ${ctx.from?.id}: ${ctx.message?.text}`);
await next(); // Continue to next middleware
});
// Admin-only middleware
bot.use(async (ctx, next) => {
if (ctx.message?.text?.startsWith("/admin")) {
if (!isAdmin(ctx.from?.id)) {
await ctx.reply("❌ Unauthorized");
return; // Stop execution
}
}
await next();
});
// Error middleware
bot.useError(async (error, ctx, next) => {
console.error("Error:", error);
await ctx.reply("Something went wrong!");
});Middleware Features:
- Runs in registration order
next()continues to the next middleware or final handlerreturn(withoutnext) stops execution- Dedicated error middleware via
useError()
Middleware Signature:
type MiddlewareFunction = (
ctx: Context,
next: () => Promise<void>
) => void | Promise<void>;
type ErrorMiddlewareFunction = (
error: any,
ctx: Context,
next: () => Promise<void>
) => void | Promise<void>;📖 API Reference
Message Methods
bot.sendMessage(chatId, text, options)
await bot.sendMessage(123456789, "Hello World!", {
parse_mode: "HTML",
disable_notification: false,
reply_markup: {
inline_keyboard: [[
{ text: "Click Me", callback_data: "click" }
]]
}
});bot.sendPhoto(chatId, photo, options)
await bot.sendPhoto(123456789, "https://example.com/photo.jpg", {
caption: "My Photo",
reply_markup: {
inline_keyboard: [[
{ text: "Like", callback_data: "like" }
]]
}
});Other Message Methods
| Method | Description |
|--------|-------------|
| bot.sendDocument(chatId, doc, options) | Send a document |
| bot.sendVideo(chatId, video, options) | Send a video |
| bot.sendAudio(chatId, audio, options) | Send audio |
| bot.sendVoice(chatId, voice, options) | Send voice message |
| bot.sendSticker(chatId, sticker, options) | Send a sticker |
| bot.sendLocation(chatId, lat, lon, options) | Send location |
| bot.sendContact(chatId, phone, name, options) | Send contact |
| bot.sendChatAction(chatId, action) | Send chat action |
| bot.forwardMessage(chatId, fromChat, msgId) | Forward message |
| bot.copyMessage(chatId, fromChat, msgId) | Copy message |
Edit Methods
bot.editMessageText(options)
await bot.editMessageText({
chat_id: 123456789,
message_id: 12345,
text: "Updated text",
reply_markup: {
inline_keyboard: [[
{ text: "New Button", callback_data: "new" }
]]
}
});bot.editMessageReplyMarkup(chatId, messageId, replyMarkup)
await bot.editMessageReplyMarkup(
123456789,
12345,
{
inline_keyboard: [[
{ text: "Button 1", callback_data: "btn1" },
{ text: "Button 2", callback_data: "btn2" }
]]
}
);Other Edit Methods
| Method | Description |
|--------|-------------|
| bot.editMessageCaption(chatId, msgId, caption) | Edit caption |
| bot.deleteMessage(chatId, msgId) | Delete message |
Chat Methods
| Method | Description |
|--------|-------------|
| bot.getChat(chatId) | Get chat info |
| bot.getChatMember(chatId, userId) | Get member info |
| bot.getChatAdministrators(chatId) | Get admins |
| bot.getChatMemberCount(chatId) | Get member count |
| bot.banChatMember(chatId, userId, untilDate?) | Ban user |
| bot.unbanChatMember(chatId, userId) | Unban user |
| bot.restrictChatMember(chatId, userId, perms) | Restrict user |
| bot.promoteChatMember(chatId, userId, options) | Promote user |
| bot.leaveChat(chatId) | Leave chat |
| bot.pinChatMessage(chatId, msgId) | Pin message |
| bot.unpinChatMessage(chatId, msgId?) | Unpin message |
Payment Methods
bot.sendStarsInvoice(options)
await bot.sendStarsInvoice({
chat_id: 123456789,
title: "Premium Access",
description: "30 days premium subscription",
payload: "premium_30days",
currency: "XTR",
prices: [
{ label: "Premium", amount: 100 }
]
});bot.answerPreCheckoutQuery(queryId, ok, errorMessage)
await bot.answerPreCheckoutQuery(
"query_id_here",
true, // Accept payment
"" // Optional error message if rejected
);Payment Handlers
// Pre-checkout validation
bot.onPreCheckoutQuery(async (query) => {
const orderExists = await checkOrder(query.invoice_payload);
return orderExists; // true = accept, false = reject
});
// Successful payment
bot.onSuccessfulPayment(async (payment) => {
console.log("Payment received:", payment);
});Keyboard Helpers
keyboard(buttons, options)
const replyKeyboard = keyboard([
["Button 1", "Button 2"],
["Button 3"]
], {
resize_keyboard: true, // Default: true
one_time_keyboard: false,
is_persistent: false,
input_field_placeholder: "Select an option"
});inlineKeyboard(buttons)
const inlineKeyboardMarkup = inlineKeyboard([
[
{ text: "Button 1", callback_data: "btn1" },
{ text: "Open URL", url: "https://example.com" }
],
[
{ text: "Open Mini App", web_app: { url: "https://app.example.com" } },
{ text: "Switch Query", switch_inline_query: "search term" }
]
]);removeKeyboard(selective)
await bot.sendMessage(chatId, "Keyboard removed", {
reply_markup: removeKeyboard()
});forceReply(placeholder, selective)
await bot.sendMessage(chatId, "Please reply", {
reply_markup: forceReply("Type your answer...")
});Bot Methods
| Method | Description |
|--------|-------------|
| bot.getMe() | Get bot info |
| bot.setMyCommands(commands) | Set command list |
| bot.getMyCommands() | Get command list |
| bot.deleteMyCommands() | Delete command list |
| bot.logOut() | Log out from cloud |
| bot.close() | Close bot instance |
| bot.start() | Start polling |
| bot.stop() | Stop polling and cleanup |
Webhook Methods
| Method | Description |
|--------|-------------|
| bot.setWebhook(options) | Set webhook URL |
| bot.deleteWebhook(dropPending?) | Delete webhook |
| bot.getWebhookInfo() | Get webhook info |
| bot.processUpdate(update) | Process an update manually |
🔧 Advanced Usage
Rate Limiting
Protect your bot from abuse:
// Configure rate limiting
bot.useRateLimit({
maxRequests: 10, // Maximum requests
windowMs: 1000, // Time window (1 second)
perUser: true, // Limit per user
perChat: false, // Limit per chat
global: false // Global limit
});Rate Limit Options:
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| maxRequests | number | 30 | Max requests per window |
| windowMs | number | 1000 | Window duration in ms |
| perUser | boolean | true | Limit per user ID |
| perChat | boolean | false | Limit per chat ID |
| global | boolean | false | Global rate limit |
Request Queue
Control API request concurrency:
bot.queue({
concurrency: 2, // Max simultaneous requests
maxSize: 100 // Max queue size
});
// High priority request
await bot.api("sendMessage", {
chat_id: chatId,
text: "Urgent message"
}, 10); // Priority 10 (higher = more priority)Queue Options:
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| concurrency | number | 1 | Max parallel requests |
| maxSize | number | 100 | Max queue size (rejects when full) |
Mini App Authentication
Backend (Node.js):
// Verify Mini App data
app.post("/api/auth", (req, res) => {
const initData = req.body.initData;
// Validate and extract user
const user = bot.verifyMiniAppInitData(initData);
if (!user) {
return res.status(401).json({ error: "Invalid authentication" });
}
// User is authenticated
console.log(user.id, user.first_name);
res.json({ user });
});Frontend (Mini App):
// Get initData from Telegram
const initData = window.Telegram.WebApp.initData;
// Send to your backend
fetch("/api/auth", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ initData })
});Webhook Setup
// Set webhook
await bot.setWebhook({
url: "https://your-server.com/webhook",
secret_token: "your-secret-token",
max_connections: 40,
allowed_updates: ["message", "callback_query"]
});
// Process updates from webhook
app.post("/webhook", async (req, res) => {
const update = req.body;
// Process update
await bot.processUpdate(update);
res.sendStatus(200);
});Inline Queries
bot.on("inline_query", async (ctx) => {
const query = ctx.update.inline_query;
const results = [
{
type: "article",
id: "1",
title: "Result 1",
input_message_content: {
message_text: "You selected result 1"
}
}
];
await bot.answerInlineQuery(query.id, results);
});⚡ Performance Optimization
Memory Management
telegrams-node-bot automatically manages memory:
// Bounded queues prevent memory exhaustion
bot.queue({
concurrency: 2,
maxSize: 100 // Rejects when full
});
// Rate limiter auto-cleans expired entries
bot.useRateLimit({
maxRequests: 10,
windowMs: 1000
});CPU Optimization
- Map-based lookups: Commands resolved in O(1) time
- No unnecessary parsing: Minimal JSON transformations
- Efficient polling: Long polling with configurable timeout
- Async processing: Non-blocking operations
Best Practices
// ✅ DO: Use specific handlers
bot.command("start", handleStart);
// ❌ DON'T: Loop through all messages
bot.on("message", async (ctx) => {
if (ctx.message?.text === "/start") {
// Slow - scans all messages
}
});
// ✅ DO: Set appropriate limits
bot.queue({ concurrency: 2, maxSize: 50 });
// ❌ DON'T: Unlimited queue
bot.queue({ concurrency: 100, maxSize: 10000 });🔒 Security Best Practices
1. Protect Your Token
// ✅ DO: Use environment variables
const bot = new LiteGram({
token: process.env.BOT_TOKEN
});
// ❌ DON'T: Hardcode tokens
const bot = new LiteGram({
token: "123456:ABC-DEF1234"
});2. Validate Mini App Data
// ✅ DO: Always validate server-side
const user = bot.verifyMiniAppInitData(initData);
// ❌ DON'T: Trust client data
const user = JSON.parse(initDataUnsafe.user);3. Validate Payments
// ✅ DO: Verify payment callbacks
bot.onPreCheckoutQuery(async (query) => {
const orderExists = await checkOrder(query.invoice_payload);
return orderExists;
});
// ❌ DON'T: Accept all payments blindly
bot.onPreCheckoutQuery(async () => true);4. Use Rate Limiting
// ✅ DO: Protect against spam
bot.useRateLimit({
maxRequests: 5,
windowMs: 1000,
perUser: true
});5. Validate User Input
bot.command("set_name", async (ctx) => {
const name = ctx.message?.text?.replace("/set_name ", "").trim();
// ✅ DO: Validate input
if (!name || name.length > 50) {
return ctx.reply("Invalid name");
}
// Save validated input
ctx.session.set({ name });
await ctx.reply("Name saved!");
});📝 TypeScript Support
telegrams-node-bot is written in TypeScript and provides full type definitions:
import {
LiteGram,
Context,
TelegramMessage,
TelegramUser,
CallbackQuery,
SendMessageOptions,
InlineKeyboardMarkup
} from "telegrams-node-bot";
// Strongly typed handlers
const handleStart = async (ctx: Context): Promise<void> => {
const user: TelegramUser | undefined = ctx.from;
const message: TelegramMessage | undefined = ctx.message;
const options: SendMessageOptions = {
parse_mode: "HTML",
reply_markup: {
inline_keyboard: [[
{ text: "Button", callback_data: "btn" }
]]
}
};
await ctx.reply("Hello!", options);
};
bot.command("start", handleStart);💡 Examples
Complete Payment Bot
import { LiteGram, keyboard, inlineKeyboard } from "telegrams-node-bot";
const bot = new LiteGram({
token: process.env.BOT_TOKEN,
polling: true
});
// Product catalog
const products = {
basic: { title: "Basic Plan", price: 50, emoji: "⭐" },
pro: { title: "Pro Plan", price: 100, emoji: "🌟" },
vip: { title: "VIP Plan", price: 500, emoji: "💎" }
};
// Show products
bot.command("products", async (ctx) => {
const buttons = Object.entries(products).map(([key, product]) => [{
text: `${product.emoji} ${product.title} - ${product.price} ⭐`,
callback_data: `buy_${key}`
}]);
await ctx.reply("Choose a product:", {
reply_markup: inlineKeyboard(buttons)
});
});
// Handle purchase
bot.on("callback_query", async (ctx) => {
const data = ctx.callbackQuery?.data;
if (data?.startsWith("buy_")) {
const productKey = data.replace("buy_", "");
const product = products[productKey];
await ctx.sendStarsInvoice({
title: product.title,
description: `${product.title} subscription`,
payload: `purchase_${productKey}`,
currency: "XTR",
prices: [{ label: product.title, amount: product.price }]
});
}
});
// Handle payments
bot.onPreCheckoutQuery(async (query) => true);
bot.onSuccessfulPayment(async (payment) => {
console.log("Payment received:", payment);
});
bot.start();Multi-Step Registration with Session
import { LiteGram } from "telegrams-node-bot";
const bot = new LiteGram({
token: process.env.BOT_TOKEN,
polling: true
});
bot.command("register", async (ctx) => {
ctx.session.set({ step: "name" });
await ctx.reply("📝 What's your name?");
});
bot.on("message", async (ctx) => {
const session = ctx.session.get();
if (session?.step === "name") {
ctx.session.update({ name: ctx.message?.text, step: "age" });
await ctx.reply("🎂 How old are you?");
} else if (session?.step === "age") {
ctx.session.update({ age: ctx.message?.text, step: "email" });
await ctx.reply("📧 What's your email?");
} else if (session?.step === "email") {
const data = {
name: session.name,
age: session.age,
email: ctx.message?.text
};
await ctx.reply(
`✅ Registration complete!\n\n` +
`Name: ${data.name}\n` +
`Age: ${data.age}\n` +
`Email: ${data.email}`
);
ctx.session.delete();
}
});
bot.start();Middleware Example
import { LiteGram } from "telegrams-node-bot";
const bot = new LiteGram({
token: process.env.BOT_TOKEN,
polling: true
});
const ADMIN_IDS = [123456, 789012];
// Logging middleware
bot.use(async (ctx, next) => {
const start = Date.now();
console.log(`📩 ${ctx.from?.id}: ${ctx.message?.text || "callback"}`);
await next();
console.log(`⏱️ ${Date.now() - start}ms`);
});
// Admin-only middleware
bot.use(async (ctx, next) => {
if (ctx.message?.text?.startsWith("/admin")) {
if (!ctx.from || !ADMIN_IDS.includes(ctx.from.id)) {
return ctx.reply("❌ Unauthorized");
}
}
await next();
});
// Error middleware
bot.useError(async (error, ctx) => {
console.error("Error:", error);
await ctx.reply("⚠️ Something went wrong. Please try again.");
});
bot.command("admin", async (ctx) => {
await ctx.reply("👑 Welcome, admin!");
});
bot.start();Mini App Authentication Server
import { LiteGram } from "telegrams-node-bot";
import http from "http";
const bot = new LiteGram({
token: process.env.BOT_TOKEN
});
const server = http.createServer((req, res) => {
if (req.url === "/auth" && req.method === "POST") {
let body = "";
req.on("data", chunk => body += chunk);
req.on("end", () => {
const { initData } = JSON.parse(body);
const user = bot.verifyMiniAppInitData(initData);
if (!user) {
res.writeHead(401);
res.end(JSON.stringify({ error: "Unauthorized" }));
return;
}
res.writeHead(200, { "Content-Type": "application/json" });
res.end(JSON.stringify({ user }));
});
}
});
server.listen(3000);🤝 Contributing
We welcome contributions! Please follow these steps:
- Fork the repository
- Create a feature branch
- Make your changes
- Run tests:
npm test - Build:
npm run build - Submit a pull request
📄 License
MIT License — see LICENSE file for details.
🔗 Links
💬 Support
- GitHub Issues: Create Issue
- Telegram Channel: @litegram
- Email: [email protected]
Made with ❤️ for the Telegram community
Remember: telegrams-node-bot is designed to be simple, fast, and efficient. Keep it lightweight!
