Browse documentation

Adding commands

Add commands to your Telegram bot on FPS.ms with Python (python-telegram-bot) or Node.js (Telegraf). Handle text, buttons, and inline queries step by step.

CanerAkar Updated
On this page

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

Feature Python (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

Problem Cause Fix
Command not working Handler not registered Make sure you added the handler before run_polling() / bot.launch()
Bot doesn't respond to messages Missing message handler or privacy mode Add a MessageHandler / bot.on('text'), or disable privacy mode in BotFather with /setprivacy
Buttons not responding Missing callback handler Register a CallbackQueryHandler / bot.action() for each callback_data
"Conflict: terminated by other getUpdates request" Multiple instances running Make sure only one instance of your bot is running

For more help, see our full troubleshooting guide.

Next steps