Creating Custom Filters

Irenogram already provides a rich set of built-in filters to handle common scenarios. However, there are times when you need more advanced or specific filtering logic. This page explains how to create your own custom filters.

Custom Filters

A custom filter is simply an async function that takes three arguments: the filter itself, the client, and the update object. The function must return True if the update should be handled, or False otherwise.

from pyrogram import filters

async def admin_filter(_, __, message):
    """Check if the sender is an admin."""
    admin_ids = [12345, 67890]
    return message.from_user and message.from_user.id in admin_ids

is_admin = filters.create(admin_filter)

You can then use this filter just like any built-in filter:

@app.on_message(is_admin)
async def admin_handler(client, message):
    await message.reply("Hello, admin!")

Filters with Arguments

Sometimes you need filters that accept arguments. You can achieve this by wrapping the filter function inside another function:

from pyrogram import filters

def chat_filter(chat_id):
    async def func(flt, _, message):
        return message.chat and message.chat.id == flt.chat_id

    return filters.create(func, chat_id=chat_id)

# Usage
@app.on_message(chat_filter(-1001234567890))
async def specific_chat(client, message):
    await message.reply("This message is from the specific chat!")

Combining Filters

Irenogram filters support Python bitwise operators to combine them:

  • filter_a & filter_b — both filters must match.

  • filter_a | filter_b — at least one filter must match.

  • ~filter_a — the filter must not match.

from pyrogram import filters

# Match private messages from admins only
@app.on_message(filters.private & is_admin)
async def private_admin(client, message):
    await message.reply("Private admin message!")

# Match messages that are NOT from bots
@app.on_message(~filters.bot)
async def no_bots(client, message):
    await message.reply("You're not a bot!")

Stateful Filters

You can also create stateful filters by storing data in the filter object itself:

from pyrogram import filters

def rate_limit_filter(max_calls, period):
    import time

    async def func(flt, _, message):
        now = time.time()
        user_id = message.from_user.id
        timestamps = flt.data.setdefault(user_id, [])

        # Remove expired timestamps
        timestamps[:] = [t for t in timestamps if now - t < flt.period]

        if len(timestamps) >= flt.max_calls:
            return False

        timestamps.append(now)
        return True

    return filters.create(func, max_calls=max_calls, period=period, data={})

Tip

Custom filters are very powerful when combined with Smart Plugins, as they allow each plugin to define its own filtering logic independently.