When working with Pyrogram, filters are the backbone of how your bot understands messages. Among them, filters.command() is one of the most commonly used because most bots rely heavily on commands like /start or /help.
In this guide, we will walk through practical ways to use command filters along with related filter techniques. The goal is to keep things simple, clear, and useful for beginners who want to build real Telegram bot features without confusion.
Filter with single commands
Handling a single command is the most common use case in Pyrogram. It is typically used for basic bot entry points such as /start or /help where only one command needs to trigger the handler.
from pyrogram import Client, filters
API_ID = 123456
API_HASH = "your_api_hash"
BOT_TOKEN = "your_bot_token"
bot = Client(
"my_bot",
api_id=API_ID,
api_hash=API_HASH,
bot_token=BOT_TOKEN
)
@app.on_message(filters.command("start"))
async def start_handler(client, message):
await message.reply("Welcome to the bot")
@app.on_message(filters.command("help"))
async def start_handler(client, message):
await message.reply("This is the help text")
app.run()Here, the handler runs only when the user sends the /start or /help commands. This approach keeps your command handling simple and is ideal for beginner-level bots.
Filter with multiple commands
Sometimes, different commands should trigger the same response. Instead of writing separate handlers, you can pass multiple command names to filters.command().
@app.on_message(filters.command(["start", "help", "begin"]))
async def start_handler(client, message):
await message.reply("Welcome to the bot")This handler will run for /start, /help, and /begin. It helps reduce duplicate code and keeps things organized.
Registering handlers without decorators
Decorators are convenient, but in modular bots you may want to attach handlers manually. Using add_handler gives you more control, especially in plugin based projects.
from pyrogram.handlers import MessageHandler
async def ping_handler(client, message):
await message.reply("Pong")
app.add_handler(
MessageHandler(ping_handler, filters.command("ping"))
)This approach is helpful when loading handlers dynamically or structuring large applications.
Combining filters with AND, OR, and NOT (bitwise operators)
Pyrogram allows you to combine filters using bitwise operators. This is very useful when your bot should respond only under specific conditions.
@app.on_message(filters.command("start") & filters.private)
async def private_start(client, message):
await message.reply("Start used in private chat")
@app.on_message(filters.command("start") | filters.command("help"))
async def multi_command(client, message):
await message.reply("Start or Help received")
@app.on_message(filters.command("start") & ( ~filters.bot & filters.private))
async def ignore_bots(client, message):
await message.reply("Hello human")Use & when both conditions must match, | when either can match, and ~ to exclude something.
Using custom command prefixes
By default, commands use the slash prefix. Pyrogram lets you define additional prefixes if your bot requires a different style.
@app.on_message(filters.command("start", prefixes=["/", "!", "."]))
async def custom_prefix(client, message):
await message.reply("Custom prefix detected")Now the handler responds to /start, !start, and .start. This is common in utility or game bots.
Case sensitivity in commands
Command matching in Pyrogram is case-insensitive by default. Users can type commands in any letter case and the bot will still catch them.
@app.on_message(filters.command("start"))
async def start_case(client, message):
await message.reply("Command detected")So /start, /Start, and /START will all trigger the same handler.
Pattern matching with regex filters
Regex filters are useful when you need flexible text matching instead of fixed commands. They allow pattern based detection.
@app.on_message(filters.regex(r"^hello"))
async def regex_handler(client, message):
await message.reply("Message starts with hello")This will trigger for messages like “hello bot” or “hello there”.
Splitting logic across multiple handlers
As your bot grows, it is better to keep different commands in separate functions. This improves readability and makes future changes easier.
@app.on_message(filters.command("start"))
async def start_cmd(client, message):
await message.reply("Start command")
@app.on_message(filters.command("help"))
async def help_cmd(client, message):
await message.reply("Help command")Each handler has a clear responsibility, which is a good practice for maintainable bots.
Creating your own custom filters
Built-in filters cover most needs, but sometimes you need custom logic. Pyrogram allows you to create your own filters easily.
i) Basic custom filter with filters.create
You can define a simple function that returns True or False and convert it into a filter.
def is_owner(_, __, message):
return message.from_user and message.from_user.id == 123456
owner_filter = filters.create(is_owner)
@app.on_message(owner_filter)
async def owner_only(client, message):
await message.reply("Hello owner")This handler runs only when the message comes from the specified user.
ii) Filters with Arguments
Custom filters can also be parameterized, which makes them reusable in different situations.
def is_user(user_id):
async def func(_, __, message):
return message.from_user and message.from_user.id == user_id
return filters.create(func)
@app.on_message(is_user(123456))
async def specific_user(client, message):
await message.reply("Matched specific user")This pattern is useful when the filtering condition depends on dynamic values.
iii) Method Calls Inside Filters
You can also perform calculated checks inside your filter, such as validating message length or content.
def is_long_message(_, __, message):
return message.text and len(message.text) > 50
long_filter = filters.create(is_long_message)
@app.on_message(long_filter)
async def long_text_handler(client, message):
await message.reply("Long message detected")This gives you full control when built-in filters are not enough.
More Filters
1. Filters with message type
Filters allow the easiest way to react to a specific message type. They are useful when your bot only needs to check conditions such as text messages or stickers.
from pyrogram import Client, filters
app = Client("my_bot")
@app.on_message(filters.text)
async def handle_text(client, message):
await message.reply("You sent a text message")
@app.on_message(filters.photo)
async def handle_sticker(client, message):
await message.reply("Image seems good")
@app.on_message(filters.sticker)
async def handle_sticker(client, message):
await message.reply("Nice sticker")
app.run()Each handler listens to only one type of update, which keeps the logic simple and readable.
2. Command arguments parsing
Handling arguments properly makes your command handlers much more useful. Most real bots depend on reading values that users pass after the command.
i) Command without args
This is the simplest case where the command is used alone. You usually just check the command and respond.
@app.on_message(filters.command("start"))
async def start_cmd(client, message):
await message.reply("Welcome to the bot")Use this when no additional user input is required.
ii) Command with args
When users pass values after the command, Pyrogram stores them in message.command. The first item is the command name and the rest are arguments.
@app.on_message(filters.command("echo"))
async def echo_cmd(client, message):
if len(message.command) > 1:
text = " ".join(message.command[1:])
await message.reply(text)This allows users to send /echo hello world and get the same text back.
iii) Optional args
Sometimes arguments are not mandatory. In that case, you check whether they exist and handle both cases.
@app.on_message(filters.command("greet"))
async def greet_cmd(client, message):
if len(message.command) > 1:
name = message.command[1]
await message.reply(f"Hello {name}")
else:
await message.reply("Hello there")This makes the command flexible and user-friendly.
iv) Required args
If your command must receive arguments, always validate and inform the user when they are missing.
@app.on_message(filters.command("ban"))
async def ban_cmd(client, message):
if len(message.command) < 2:
await message.reply("Usage: /ban user_id")
return
user_id = message.command[1]
await message.reply(f"User {user_id} banned")Clear validation prevents unexpected errors in your bot.
v) Multiple args
Some commands require more than one value. You can extract them from the command list.
@app.on_message(filters.command("setprice"))
async def set_price(client, message):
if len(message.command) < 3:
await message.reply("Usage: /setprice item price")
return
item = message.command[1]
price = message.command[2]
await message.reply(f"{item} price set to {price}")This is common in admin or configuration commands.
vi) Edge cases (empty args)
Users may send a command with trailing spaces, but no real argument. Always strip and validate input.
@app.on_message(filters.command("echo"))
async def echo_edge(client, message):
if len(message.command) <= 1 or not message.command[1].strip():
await message.reply("Please provide text to echo")
return
await message.reply(" ".join(message.command[1:]))This prevents empty or meaningless inputs from being processed.
3. Command with other common filters
Combining command filters with other filters helps you restrict when a command should work. This is very useful in real world bots.
i) with filters.me
This filter allows only messages sent by your own account. It is mostly used in userbots.
@app.on_message(filters.command("ping") & filters.me)
async def self_ping(client, message):
await message.reply("Pong from self")Use this when the command should respond only to your own messages.
ii) with filters.bot
This filter matches messages sent by bots. It is helpful when your bot interacts with other bots.
@app.on_message(filters.command("check") & filters.bot)
async def bot_check(client, message):
await message.reply("Message from a bot detected")This is rarely needed but useful in automation setups.
iii) with filters.forwarded
You can restrict commands to only forwarded messages.
@app.on_message(filters.command("info") & filters.forwarded)
async def forwarded_only(client, message):
await message.reply("This was a forwarded message")Helpful when your logic depends on forwarded content.
iii) with filters.edited
This filter triggers when a command appears in an edited message.
@app.on_message(filters.command("update") & filters.edited)
async def edited_cmd(client, message):
await message.reply("Edited command detected")Useful for bots that must track message updates.
iv) with filters.reply
This ensures the command is used while replying to another message.
@app.on_message(filters.command("quote") & filters.reply)
async def reply_cmd(client, message):
await message.reply("You replied with a command")Common in moderation and context based features.
v) with filters.media
You can require that the command message contains media.
@app.on_message(filters.command("analyze") & filters.media)
async def media_cmd(client, message):
await message.reply("Media received with command")Useful for image processing or file based bots.
4. Edge Cases
Handling edge cases makes your bot more robust and prevents unexpected behavior.
i) Command with extra spaces
Users may type extra spaces after commands. Always rely on parsed command values rather than raw text.
@app.on_message(filters.command("start"))
async def start_spaces(client, message):
await message.reply("Handled even with extra spaces")Pyrogram usually handles spacing well, but validation is still a good practice.
ii) Command with newline
Sometimes users send commands followed by a newline. Your handler should still work.
@app.on_message(filters.command("note"))
async def newline_cmd(client, message):
await message.reply("Newline command handled")This ensures consistent behavior across different user inputs.
iii) Command in caption vs text
Commands can appear in media captions as well as normal text. Pyrogram handles both automatically.
@app.on_message(filters.command("captiontest")) # send a image with the caption /captiontest
async def caption_cmd(client, message):
await message.reply("Command detected in text or caption")This is important for bots that process media posts.
iv) Commands in edited messages
If users edit a message to include a command, you can catch it using the edited filter.
@app.on_message(filters.command("fix") & filters.edited)
async def edited_detect(client, message):
await message.reply("Command added via edit")Useful for moderation and tracking changes.
v) Commands from anonymous admins
In groups, anonymous admins may not have a visible user id. Always check safely before accessing user fields.
@app.on_message(filters.command("admincheck"))
async def admin_check(client, message):
if not message.from_user:
await message.reply("Anonymous admin detected")This prevents attribute errors and makes your bot more stable.
Conclusion:
filters.command in Pyrogram is essential for building structured and reliable Telegram bots. From handling a simple /start command to combining filters, parsing arguments, and managing edge cases, each technique adds more control to your bot logic.
Once you are comfortable with these patterns, you can design cleaner handlers, reduce duplicate code, and build bots that behave exactly as expected. Start with simple commands, then gradually apply advanced filter combinations as your project grows.
FAQ
1. What is filters.command in Pyrogram?
filters.command is used to detect Telegram bot commands like /start or /help. It allows your bot to trigger specific handlers when users send those commands.
2. Is Pyrogram filters.command case sensitive?
No. Command matching in Pyrogram is case insensitive by default. This means /start, /Start, and /START will all work the same.
3. How can I handle multiple commands with one handler?
You can pass a list of command names to filters.command. This lets a single function respond to multiple commands without writing separate handlers.
4. Can I use custom prefixes instead of slash?
Yes. Pyrogram allows you to define custom prefixes such as ! or . using the prefixes parameter inside filters.command.
5. How do I read command arguments in Pyrogram?
Command arguments are available in message.command. The first item is the command name and the remaining items are the arguments sent by the user.
6. Can filters.command work with other filters?
Yes. You can combine it with filters like filters.private, filters.group, filters.reply, and others using bitwise operators such as &, |, and ~.
Also Read:
