Why Telegram bots?
Telegram is uniquely suited for bot development. Unlike other messaging platforms, Telegram offers:
- A clean, well-documented Bot API
- No approval process for bot creation
- Support for rich messages (inline keyboards, media, polls)
- Webhook and polling modes
- Free, unlimited messaging
- A built-in payment system
For businesses, Telegram bots are an excellent way to provide customer support, send notifications, and build conversational interfaces.
Creating your bot
Step 1: Register with BotFather
- Open Telegram and search for
@BotFather - Send
/newbot - Choose a name and username (must end in
bot) - Save the API token BotFather gives you
Step 2: Set up your project
mkdir my-telegram-bot && cd my-telegram-bot
npm init -y
npm install node-telegram-bot-api express dotenv
Step 3: Your first bot
import TelegramBot from 'node-telegram-bot-api';
import dotenv from 'dotenv';
dotenv.config();
const bot = new TelegramBot(process.env.BOT_TOKEN, { polling: true });
bot.onText(/\/start/, (msg) => {
const chatId = msg.chat.id;
bot.sendMessage(chatId, 'Welcome! I can help you track orders. Send /track <order_number> to get started.');
});
bot.onText(/\/track (.+)/, (msg, match) => {
const chatId = msg.chat.id;
const orderNumber = match[1];
bot.sendMessage(chatId, `Looking up order ${orderNumber}...`);
// Fetch order status and respond
});
console.log('Bot is running...');
Using inline keyboards
Inline keyboards are one of Telegram’s most powerful features — they let you add interactive buttons to messages:
bot.onText(/\/menu/, (msg) => {
const chatId = msg.chat.id;
const options = {
reply_markup: {
inline_keyboard: [
[
{ text: 'Track Order', callback_data: 'track' },
{ text: 'Contact Support', callback_data: 'support' },
],
[
{ text: 'FAQ', callback_data: 'faq' },
{ text: 'Settings', callback_data: 'settings' },
],
],
},
};
bot.sendMessage(chatId, 'How can I help you?', options);
});
bot.on('callback_query', (query) => {
const chatId = query.message.chat.id;
const action = query.data;
switch (action) {
case 'track':
bot.sendMessage(chatId, 'Please enter your order number:');
break;
case 'support':
bot.sendMessage(chatId, 'A support agent will join this chat shortly.');
break;
case 'faq':
bot.sendMessage(chatId, 'Check our FAQ at https://example.com/faq');
break;
}
bot.answerCallbackQuery(query.id);
});
Switching to webhooks for production
Polling is fine for development, but for production you should use webhooks. They’re more efficient and scalable.
import express from 'express';
import TelegramBot from 'node-telegram-bot-api';
const app = express();
app.use(express.json());
const bot = new TelegramBot(process.env.BOT_TOKEN);
// Set webhook
bot.setWebHook(`${process.env.WEBHOOK_URL}/bot${process.env.BOT_TOKEN}`);
// Handle webhook requests
app.post(`/bot${process.env.BOT_TOKEN}`, (req, res) => {
bot.processUpdate(req.body);
res.sendStatus(200);
});
// Bot handlers
bot.onText(/\/start/, (msg) => {
bot.sendMessage(msg.chat.id, 'Hello! How can I help you?');
});
app.listen(3000, () => console.log('Server running on port 3000'));
Session management
For conversational bots, you need to track conversation state per user. I use Redis for this:
import Redis from 'ioredis';
const redis = new Redis(process.env.REDIS_URL);
async function getSession(userId) {
const session = await redis.get(`session:${userId}`);
return session ? JSON.parse(session) : { state: 'initial', data: {} };
}
async function saveSession(userId, session) {
await redis.set(`session:${userId}`, JSON.stringify(session), 'EX', 3600);
}
bot.on('message', async (msg) => {
const userId = msg.from.id;
const session = await getSession(userId);
// Handle message based on session state
switch (session.state) {
case 'initial':
// Show main menu
break;
case 'awaiting_order_number':
session.data.orderNumber = msg.text;
const status = await getOrderStatus(msg.text);
bot.sendMessage(userId, `Order ${msg.text} status: ${status}`);
session.state = 'initial';
break;
}
await saveSession(userId, session);
});
Deployment with Docker
FROM node:20-slim
WORKDIR /app
COPY package*.json ./
RUN npm ci --production
COPY . .
EXPOSE 3000
CMD ["node", "index.js"]
# docker-compose.yml
version: '3'
services:
bot:
build: .
ports:
- "3000:3000"
env_file: .env
depends_on:
- redis
restart: unless-stopped
redis:
image: redis:7-alpine
volumes:
- redis-data:/data
restart: unless-stopped
volumes:
redis-data:
Best practices
- Always handle errors: Unhandled errors will crash your bot and miss messages
- Use queues for high volume: If your bot handles thousands of messages, use a message queue
- Implement rate limiting: Telegram limits you to 30 messages per second
- Log everything: You’ll need logs to debug issues with specific users
- Use TypeScript: Type safety prevents many runtime errors
- Set up monitoring: Use a health check endpoint and uptime monitoring
Conclusion
Telegram bots are one of the easiest and most powerful ways to build conversational interfaces. With Node.js and the Bot API, you can go from zero to production in a matter of hours. The key to a great bot is not the technology — it’s designing a conversation flow that genuinely helps your users.