Telegram Bots

Adding commands

CanerAkar Mar 17, 2026

Commands are how users interact with your Telegram bot. When a user sends /start, /help, or any custom command, your bot receives it and responds. This guide shows you how to add commands, handle messages, and create interactive buttons for your bot hosted on FPS.ms.

Prerequisites

Before adding commands, make sure you:
  1. Have your bot installed and running on FPS.ms
  2. Have created your bot with BotFather
  3. Have your bot token set up in a .env file

Python (python-telegram-bot)

Basic commands

app.py
import os
from dotenv import load_dotenv
from telegram import Update
from telegram.ext import ApplicationBuilder, CommandHandler, MessageHandler, filters, ContextTypes

load_dotenv()

async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
    user = update.effective_user
    await update.message.reply_text(f'Welcome, {user.first_name}! Type /help to see what I can do.')

async def help_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
    help_text = (
        'Here are my commands:\n'
        '/start - Start the bot\n'
        '/help - Show this message\n'
        '/ping - Check if I am online\n'
        '/echo <text> - I will repeat your text'
    )
    await update.message.reply_text(help_text)

async def ping(update: Update, context: ContextTypes.DEFAULT_TYPE):
    await update.message.reply_text('Pong!')

async def echo(update: Update, context: ContextTypes.DEFAULT_TYPE):
    if context.args:
        await update.message.reply_text(' '.join(context.args))
    else:
        await update.message.reply_text('Usage: /echo <text>')

def main():
    app = ApplicationBuilder().token(os.environ['BOT_TOKEN']).build()

    app.add_handler(CommandHandler('start', start))
    app.add_handler(CommandHandler('help', help_command))
    app.add_handler(CommandHandler('ping', ping))
    app.add_handler(CommandHandler('echo', echo))

    print('Bot is running...')
    app.run_polling()

if __name__ == '__main__':
    main()

How it works

  1. CommandHandler('start', start) — registers a handler for the /start command
  2. context.args — contains the text after the command (e.g., /echo hello world gives ['hello', 'world'])
  3. update.effective_user — the user who sent the message
  4. app.run_polling() — starts listening for messages using polling

Handling regular messages

To respond to regular text messages (not commands), use MessageHandler:
Message handler
async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE):
    text = update.message.text
    await update.message.reply_text(f'You said: {text}')

# Add after your command handlers
app.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, handle_message))

Inline keyboard buttons

Add interactive buttons to your messages:
Inline keyboard
from telegram import InlineKeyboardButton, InlineKeyboardMarkup
from telegram.ext import CallbackQueryHandler

async def menu(update: Update, context: ContextTypes.DEFAULT_TYPE):
    keyboard = [
        [InlineKeyboardButton('Option 1', callback_data='option_1')],
        [InlineKeyboardButton('Option 2', callback_data='option_2')],
        [InlineKeyboardButton('Visit FPS.ms', url='https://fps.ms')],
    ]
    reply_markup = InlineKeyboardMarkup(keyboard)
    await update.message.reply_text('Choose an option:', reply_markup=reply_markup)

async def button_callback(update: Update, context: ContextTypes.DEFAULT_TYPE):
    query = update.callback_query
    await query.answer()
    await query.edit_message_text(f'You selected: {query.data}')

# Register handlers
app.add_handler(CommandHandler('menu', menu))
app.add_handler(CallbackQueryHandler(button_callback))

Node.js (Telegraf)

Basic commands

index.js
require('dotenv').config();
const { Telegraf } = require('telegraf');

const bot = new Telegraf(process.env.BOT_TOKEN);

bot.start((ctx) => {
    ctx.reply(`Welcome, ${ctx.from.first_name}! Type /help to see what I can do.`);
});

bot.help((ctx) => {
    ctx.reply(
        'Here are my commands:\n' +
        '/start - Start the bot\n' +
        '/help - Show this message\n' +
        '/ping - Check if I am online\n' +
        '/echo <text> - I will repeat your text'
    );
});

bot.command('ping', (ctx) => {
    ctx.reply('Pong!');
});

bot.command('echo', (ctx) => {
    const text = ctx.message.text.split(' ').slice(1).join(' ');
    if (text) {
        ctx.reply(text);
    } else {
        ctx.reply('Usage: /echo <text>');
    }
});

bot.launch();
console.log('Bot is running...');

process.once('SIGINT', () => bot.stop('SIGINT'));
process.once('SIGTERM', () => bot.stop('SIGTERM'));

Handling regular messages

Message handler
bot.on('text', (ctx) => {
    ctx.reply(`You said: ${ctx.message.text}`);
});
Handler order matters
Place the bot.on('text') handler after all your bot.command() handlers. Telegraf processes handlers in order, and you don't want the text handler to catch commands.

Inline keyboard buttons

Inline keyboard
const { Markup } = require('telegraf');

bot.command('menu', (ctx) => {
    ctx.reply('Choose an option:', Markup.inlineKeyboard([
        [Markup.button.callback('Option 1', 'option_1')],
        [Markup.button.callback('Option 2', 'option_2')],
        [Markup.button.url('Visit FPS.ms', 'https://fps.ms')],
    ]));
});

bot.action('option_1', (ctx) => {
    ctx.answerCbQuery();
    ctx.editMessageText('You selected: Option 1');
});

bot.action('option_2', (ctx) => {
    ctx.answerCbQuery();
    ctx.editMessageText('You selected: Option 2');
});

Register commands in BotFather

After adding commands in your code, register them in BotFather so they appear in Telegram's command menu:
  1. Open @BotFather in Telegram
  2. Type /setcommands
  3. Select your bot
  4. Send your commands:
start - Start the bot
help - Show available commands
ping - Check if the bot is online
echo - Repeat your text
menu - Show interactive menu
See the BotFather guide for more details.

Python vs Node.js command handling

FeaturePython (python-telegram-bot)Node.js (Telegraf)
Command handler`CommandHandler('ping', ping)``bot.command('ping', handler)`
Message handler`MessageHandler(filters.TEXT, handler)``bot.on('text', handler)`
Get command args`context.args``ctx.message.text.split(' ').slice(1)`
Reply to user`update.message.reply_text()``ctx.reply()`
Inline keyboard`InlineKeyboardMarkup``Markup.inlineKeyboard()`
Button callback`CallbackQueryHandler``bot.action()`

Troubleshooting

ProblemCauseFix
Command not workingHandler not registeredMake sure you added the handler before `run_polling()` / `bot.launch()`
Bot doesn't respond to messagesMissing message handler or privacy modeAdd a `MessageHandler` / `bot.on('text')`, or disable privacy mode in BotFather with `/setprivacy`
Buttons not respondingMissing callback handlerRegister a `CallbackQueryHandler` / `bot.action()` for each `callback_data`
"Conflict: terminated by other getUpdates request"Multiple instances runningMake sure only one instance of your bot is running
For more help, see our full troubleshooting guide.

Next steps

Was this article helpful?