---
title: "Telegram Bot Commands - Python & Node.js Guide | FPS.ms"
description: "Add commands to your Telegram bot hosted on FPS.ms. Step-by-step guide for python-telegram-bot and Telegraf with handlers, buttons, and replies."
url: https://fps.ms/docs/telegram-bots/adding-commands
---
# Adding commands

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](https://fps.ms/docs/telegram-bots/installation.md)
2. Have [created your bot with BotFather](https://fps.ms/docs/telegram-bots/creating-your-bot-with-botfather.md)
3. Have your [bot token set up](https://fps.ms/docs/telegram-bots/using-environment-variables.md) in a `.env` file

## Python (python-telegram-bot)

### Basic commands

```py title="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`:

```py title="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:

```py title="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

```js title="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

```js title="Message handler"
bot.on('text', (ctx) => {
    ctx.reply(`You said: ${ctx.message.text}`);
});
```

!!! warning "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

```js title="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](https://t.me/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](https://fps.ms/docs/telegram-bots/creating-your-bot-with-botfather.md) 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](https://fps.ms/docs/telegram-bots/troubleshooting.md).

## Next steps

- [Install your bot](https://fps.ms/docs/telegram-bots/installation.md) — full setup for Python and Node.js
- [Edit your code](https://fps.ms/docs/telegram-bots/editing-code.md) — update files via SFTP or VS Code
- [Set up environment variables](https://fps.ms/docs/telegram-bots/using-environment-variables.md) — manage tokens and config securely
- [Create a schedule](https://fps.ms/docs/getting-started/creating-schedules.md) — set up automatic restarts

---

[FPS.ms](https://fps.ms/index.html.md) · [All pages](https://fps.ms/llms.txt) · [Documentation](https://fps.ms/docs/llms.txt)
