A pure Python, asynchronous interface for the Telegram Bot API with comprehensive wrapper and high-level framework for building sophisticated Telegram bots
—
Powerful filtering system for precisely targeting which updates handlers should process, with extensive built-in filters and support for custom filter creation.
Core filters for common update types and content.
class filters:
ALL: UpdateFilter
TEXT: MessageFilter
COMMAND: MessageFilter
PHOTO: MessageFilter
VIDEO: MessageFilter
VIDEO_NOTE: MessageFilter
VOICE: MessageFilter
AUDIO: MessageFilter
DOCUMENT: MessageFilter
ANIMATION: MessageFilter
STICKER: MessageFilter
LOCATION: MessageFilter
CONTACT: MessageFilter
VENUE: MessageFilter
POLL: MessageFilter
DICE: MessageFilter
GAME: MessageFilter
FORWARDED: MessageFilter
REPLY: MessageFilterUsage examples:
from telegram.ext import MessageHandler, filters
# Text messages only (excluding commands)
text_handler = MessageHandler(filters.TEXT & ~filters.COMMAND, handle_text)
# Photo messages
photo_handler = MessageHandler(filters.PHOTO, handle_photo)
# Video or video note messages
video_handler = MessageHandler(filters.VIDEO | filters.VIDEO_NOTE, handle_video)
# Documents that aren't photos/videos/audio
doc_handler = MessageHandler(
filters.DOCUMENT & ~(filters.PHOTO | filters.VIDEO | filters.AUDIO),
handle_document
)
# Forwarded messages
forward_handler = MessageHandler(filters.FORWARDED, handle_forwarded)
# Replies to other messages
reply_handler = MessageHandler(filters.REPLY, handle_reply)Filter messages based on chat type.
class filters:
class ChatType:
PRIVATE: MessageFilter
GROUP: MessageFilter
SUPERGROUP: MessageFilter
CHANNEL: MessageFilter
GROUPS: MessageFilter # GROUP | SUPERGROUPUsage examples:
# Private messages only
private_handler = MessageHandler(
filters.ChatType.PRIVATE & filters.TEXT,
handle_private_message
)
# Group messages (both regular and supergroups)
group_handler = MessageHandler(
filters.ChatType.GROUPS & filters.COMMAND,
handle_group_command
)
# Channel posts
channel_handler = MessageHandler(
filters.ChatType.CHANNEL,
handle_channel_post
)Filter by specific users, chats, or user properties.
class filters:
class User:
@staticmethod
def user_id(user_id: int | list[int]) -> MessageFilter: ...
@staticmethod
def username(username: str | list[str]) -> MessageFilter: ...
class Chat:
@staticmethod
def chat_id(chat_id: int | list[int]) -> MessageFilter: ...
@staticmethod
def username(username: str | list[str]) -> MessageFilter: ...
@staticmethod
def title(title: str | list[str]) -> MessageFilter: ...
IS_BOT: MessageFilter
IS_PREMIUM: MessageFilterUsage examples:
# Messages from specific user
user_filter = filters.User.user_id(123456789)
user_handler = MessageHandler(user_filter, handle_specific_user)
# Messages from multiple users
admin_users = [123456789, 987654321, 555666777]
admin_filter = filters.User.user_id(admin_users)
admin_handler = MessageHandler(admin_filter & filters.COMMAND, handle_admin_command)
# Messages from specific username
username_filter = filters.User.username("john_doe")
username_handler = MessageHandler(username_filter, handle_john)
# Messages in specific chat
chat_filter = filters.Chat.chat_id(-1001234567890)
chat_handler = MessageHandler(chat_filter, handle_specific_chat)
# Messages from bots
bot_filter = filters.IS_BOT
bot_handler = MessageHandler(bot_filter, handle_bot_message)
# Messages from premium users
premium_filter = filters.IS_PREMIUM
premium_handler = MessageHandler(premium_filter, handle_premium_user)Filter based on message content and properties.
class filters:
class Text:
@staticmethod
def startswith(prefix: str | list[str], ignore_case: bool = False) -> MessageFilter: ...
@staticmethod
def endswith(suffix: str | list[str], ignore_case: bool = False) -> MessageFilter: ...
@staticmethod
def contains(substring: str | list[str], ignore_case: bool = False) -> MessageFilter: ...
class Regex:
@staticmethod
def create(pattern: str | Pattern, flags: int = 0) -> MessageFilter: ...
class Language:
@staticmethod
def language_code(lang_code: str | list[str]) -> MessageFilter: ...
HAS_MEDIA_SPOILER: MessageFilter
HAS_PROTECTED_CONTENT: MessageFilter
IS_AUTOMATIC_FORWARD: MessageFilter
IS_TOPIC_MESSAGE: MessageFilterUsage examples:
import re
# Messages starting with specific text
hello_filter = filters.Text.startswith("hello", ignore_case=True)
hello_handler = MessageHandler(hello_filter, handle_greeting)
# Messages containing keywords
keyword_filter = filters.Text.contains(["help", "support", "assistance"])
help_handler = MessageHandler(keyword_filter, handle_help_request)
# Regex pattern matching
email_pattern = r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b'
email_filter = filters.Regex.create(email_pattern, re.IGNORECASE)
email_handler = MessageHandler(email_filter, handle_email)
# Messages from users with specific language
spanish_filter = filters.Language.language_code("es")
spanish_handler = MessageHandler(spanish_filter & filters.TEXT, handle_spanish)
# Messages with spoiler media
spoiler_filter = filters.HAS_MEDIA_SPOILER
spoiler_handler = MessageHandler(spoiler_filter, handle_spoiler_media)Filter messages containing specific entity types.
class filters:
class Entity:
MENTION: MessageFilter # @username
HASHTAG: MessageFilter # #hashtag
CASHTAG: MessageFilter # $CASHTAG
BOT_COMMAND: MessageFilter # /command
URL: MessageFilter # http://example.com
EMAIL: MessageFilter # user@example.com
PHONE_NUMBER: MessageFilter # +1234567890
BOLD: MessageFilter # **bold**
ITALIC: MessageFilter # *italic*
CODE: MessageFilter # `code`
PRE: MessageFilter # ```pre```
TEXT_LINK: MessageFilter # [text](url)
TEXT_MENTION: MessageFilter # @user (no username)Usage examples:
# Messages with mentions
mention_handler = MessageHandler(filters.Entity.MENTION, handle_mention)
# Messages with hashtags
hashtag_handler = MessageHandler(filters.Entity.HASHTAG, handle_hashtag)
# Messages with URLs
url_handler = MessageHandler(filters.Entity.URL, handle_url)
# Messages with email addresses
email_handler = MessageHandler(filters.Entity.EMAIL, handle_email)
# Messages with phone numbers
phone_handler = MessageHandler(filters.Entity.PHONE_NUMBER, handle_phone)
# Messages with code blocks
code_handler = MessageHandler(filters.Entity.CODE | filters.Entity.PRE, handle_code)Filter special message types and status updates.
class filters:
class StatusUpdate:
NEW_CHAT_MEMBERS: MessageFilter
LEFT_CHAT_MEMBER: MessageFilter
NEW_CHAT_TITLE: MessageFilter
NEW_CHAT_PHOTO: MessageFilter
DELETE_CHAT_PHOTO: MessageFilter
GROUP_CHAT_CREATED: MessageFilter
SUPERGROUP_CHAT_CREATED: MessageFilter
CHANNEL_CHAT_CREATED: MessageFilter
MIGRATE_TO_CHAT_ID: MessageFilter
MIGRATE_FROM_CHAT_ID: MessageFilter
PINNED_MESSAGE: MessageFilter
INVOICE: MessageFilter
SUCCESSFUL_PAYMENT: MessageFilter
USERS_SHARED: MessageFilter
CHAT_SHARED: MessageFilter
CONNECTED_WEBSITE: MessageFilter
WRITE_ACCESS_ALLOWED: MessageFilter
PASSPORT_DATA: MessageFilter
PROXIMITY_ALERT_TRIGGERED: MessageFilter
VIDEO_CHAT_SCHEDULED: MessageFilter
VIDEO_CHAT_STARTED: MessageFilter
VIDEO_CHAT_ENDED: MessageFilter
VIDEO_CHAT_PARTICIPANTS_INVITED: MessageFilter
WEB_APP_DATA: MessageFilter
FORUM_TOPIC_CREATED: MessageFilter
FORUM_TOPIC_CLOSED: MessageFilter
FORUM_TOPIC_REOPENED: MessageFilter
FORUM_TOPIC_EDITED: MessageFilter
GENERAL_FORUM_TOPIC_HIDDEN: MessageFilter
GENERAL_FORUM_TOPIC_UNHIDDEN: MessageFilter
GIVEAWAY_CREATED: MessageFilter
GIVEAWAY: MessageFilter
GIVEAWAY_WINNERS: MessageFilter
GIVEAWAY_COMPLETED: MessageFilter
CHAT_BACKGROUND_SET: MessageFilter
BOOST_ADDED: MessageFilterUsage examples:
# New members joining
welcome_handler = MessageHandler(
filters.StatusUpdate.NEW_CHAT_MEMBERS,
handle_new_members
)
# Members leaving
goodbye_handler = MessageHandler(
filters.StatusUpdate.LEFT_CHAT_MEMBER,
handle_member_left
)
# Chat title changes
title_handler = MessageHandler(
filters.StatusUpdate.NEW_CHAT_TITLE,
handle_title_change
)
# Successful payments
payment_handler = MessageHandler(
filters.StatusUpdate.SUCCESSFUL_PAYMENT,
handle_payment_success
)
# Video chat events
video_chat_handler = MessageHandler(
filters.StatusUpdate.VIDEO_CHAT_STARTED | filters.StatusUpdate.VIDEO_CHAT_ENDED,
handle_video_chat_event
)Filter different types of updates beyond messages.
class filters:
class UpdateType:
MESSAGE: UpdateFilter
EDITED_MESSAGE: UpdateFilter
CHANNEL_POST: UpdateFilter
EDITED_CHANNEL_POST: UpdateFilter
INLINE_QUERY: UpdateFilter
CHOSEN_INLINE_RESULT: UpdateFilter
CALLBACK_QUERY: UpdateFilter
SHIPPING_QUERY: UpdateFilter
PRE_CHECKOUT_QUERY: UpdateFilter
POLL: UpdateFilter
POLL_ANSWER: UpdateFilter
MY_CHAT_MEMBER: UpdateFilter
CHAT_MEMBER: UpdateFilter
CHAT_JOIN_REQUEST: UpdateFilter
CHAT_BOOST: UpdateFilter
REMOVED_CHAT_BOOST: UpdateFilter
BUSINESS_CONNECTION: UpdateFilter
BUSINESS_MESSAGE: UpdateFilter
EDITED_BUSINESS_MESSAGE: UpdateFilter
DELETED_BUSINESS_MESSAGES: UpdateFilter
MESSAGE_REACTION: UpdateFilter
MESSAGE_REACTION_COUNT: UpdateFilter
PURCHASED_PAID_MEDIA: UpdateFilterCreate custom filters for specific use cases.
class BaseFilter:
def __init__(self, name: str = None, data_filter: bool = False): ...
def filter(self, message: Message) -> bool | dict: ...
def __and__(self, other: BaseFilter) -> BaseFilter: ...
def __or__(self, other: BaseFilter) -> BaseFilter: ...
def __invert__(self) -> BaseFilter: ...
def __xor__(self, other: BaseFilter) -> BaseFilter: ...
class MessageFilter(BaseFilter):
pass
class UpdateFilter(BaseFilter):
passUsage examples:
from telegram.ext.filters import BaseFilter
# Custom filter for long messages
class LongMessageFilter(BaseFilter):
def __init__(self, min_length=100):
self.min_length = min_length
super().__init__(name=f"LongMessage({min_length})")
def filter(self, message):
return message.text and len(message.text) >= self.min_length
long_msg_filter = LongMessageFilter(200)
long_handler = MessageHandler(long_msg_filter, handle_long_message)
# Filter for specific file extensions
class FileExtensionFilter(BaseFilter):
def __init__(self, extensions):
self.extensions = [ext.lower() for ext in extensions]
super().__init__(name=f"FileExtension({extensions})")
def filter(self, message):
if not message.document or not message.document.file_name:
return False
filename = message.document.file_name.lower()
return any(filename.endswith(f".{ext}") for ext in self.extensions)
pdf_filter = FileExtensionFilter(["pdf"])
pdf_handler = MessageHandler(pdf_filter, handle_pdf)
# Filter with data extraction
class UrlExtractorFilter(BaseFilter):
def __init__(self):
super().__init__(name="URLExtractor", data_filter=True)
def filter(self, message):
if not message.entities:
return False
urls = []
for entity in message.entities:
if entity.type == "url":
url = message.text[entity.offset:entity.offset + entity.length]
urls.append(url)
return {"urls": urls} if urls else False
url_filter = UrlExtractorFilter()
async def handle_urls(update, context):
urls = context.match["urls"] # Access extracted data
await update.message.reply_text(f"Found URLs: {', '.join(urls)}")
url_handler = MessageHandler(url_filter, handle_urls)
# Admin filter
class AdminFilter(BaseFilter):
def __init__(self):
super().__init__(name="Admin")
def filter(self, message):
# Check if user is admin in the chat
return message.from_user.id in get_admin_list(message.chat.id)
admin_filter = AdminFilter()
admin_handler = MessageHandler(admin_filter & filters.COMMAND, handle_admin_command)Combine filters using logical operators.
# AND operation - both conditions must be true
text_and_private = filters.TEXT & filters.ChatType.PRIVATE
# OR operation - either condition can be true
media_filter = filters.PHOTO | filters.VIDEO | filters.ANIMATION
# NOT operation - condition must be false
non_command_text = filters.TEXT & ~filters.COMMAND
# XOR operation - exactly one condition must be true
either_photo_or_video = filters.PHOTO ^ filters.VIDEO
# Complex combinations
admin_media_filter = (
filters.User.user_id(admin_user_ids) &
(filters.PHOTO | filters.VIDEO | filters.DOCUMENT) &
~filters.HAS_MEDIA_SPOILER
)
# Grouped conditions
content_filter = (
(filters.TEXT & filters.Text.contains(["urgent", "important"])) |
(filters.PHOTO & filters.Entity.HASHTAG) |
(filters.DOCUMENT & filters.Text.contains("report"))
)# Order filters by specificity (most specific first)
# Good - specific user check first
efficient_filter = filters.User.user_id(admin_id) & filters.TEXT & filters.COMMAND
# Less efficient - broad check first
less_efficient = filters.TEXT & filters.COMMAND & filters.User.user_id(admin_id)
# Use built-in filters when possible (they're optimized)
# Good
builtin_filter = filters.PHOTO
# Less efficient custom equivalent
class PhotoFilter(BaseFilter):
def filter(self, message):
return message.photo is not None
# Cache expensive operations in custom filters
class ExpensiveFilter(BaseFilter):
def __init__(self):
self._cache = {}
super().__init__()
def filter(self, message):
user_id = message.from_user.id
if user_id not in self._cache:
self._cache[user_id] = expensive_check(user_id)
return self._cache[user_id]from typing import Union, List, Pattern
import re
FilterType = Union[BaseFilter, MessageFilter, UpdateFilter]
PatternType = Union[str, Pattern]Install with Tessl CLI
npx tessl i tessl/pypi-python-telegram-bot