Skip to content
33 changes: 19 additions & 14 deletions pyrogram/file_id.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,11 @@ class ThumbnailSource(IntEnum):
CHAT_PHOTO_SMALL = 2 # DialogPhotoSmall
CHAT_PHOTO_BIG = 3 # DialogPhotoBig
STICKER_SET_THUMBNAIL = 4
FULL_LEGACY = 5
DIALOGPHOTO_SMALL_LEGACY = 6
DIALOGPHOTO_BIG_LEGACY = 7
STICKERSET_THUMBNAIL_LEGACY = 8
STICKERSET_THUMBNAIL_VERSION = 9


# Photo-like file ids are longer and contain extra info, the rest are all documents
Expand Down Expand Up @@ -246,16 +251,16 @@ def decode(file_id: str):
media_id, access_hash = struct.unpack("<qq", buffer.read(16))

if file_type in PHOTO_TYPES:
volume_id, = struct.unpack("<q", buffer.read(8))
thumbnail_source, = (0,) if major < 4 else struct.unpack("<i", buffer.read(4))
volume_id = struct.unpack('<q', buffer.read(8))[0] if minor < 32 else None
thumbnail_source = 0 if major < 4 else struct.unpack("<i", buffer.read(4))[0]

try:
thumbnail_source = ThumbnailSource(thumbnail_source)
except ValueError:
raise ValueError(f"Unknown thumbnail_source {thumbnail_source} of file_id {file_id}")

if thumbnail_source == ThumbnailSource.LEGACY:
secret, local_id = struct.unpack("<qi", buffer.read(12))
secret = struct.unpack("<q", buffer.read(8))[0]

return FileId(
major=major,
Expand All @@ -268,14 +273,13 @@ def decode(file_id: str):
volume_id=volume_id,
thumbnail_source=thumbnail_source,
secret=secret,
local_id=local_id
)

if thumbnail_source == ThumbnailSource.THUMBNAIL:
thumbnail_file_type, thumbnail_size, local_id = struct.unpack("<iii", buffer.read(12))
thumbnail_file_type, thumbnail_size = struct.unpack("<ii", buffer.read(8))
thumbnail_size = chr(thumbnail_size)

return FileId(
fileId = FileId(
major=major,
minor=minor,
file_type=file_type,
Expand All @@ -287,13 +291,13 @@ def decode(file_id: str):
thumbnail_source=thumbnail_source,
thumbnail_file_type=thumbnail_file_type,
thumbnail_size=thumbnail_size,
local_id=local_id
)

if thumbnail_source in (ThumbnailSource.CHAT_PHOTO_SMALL, ThumbnailSource.CHAT_PHOTO_BIG):
chat_id, chat_access_hash, local_id = struct.unpack("<qqi", buffer.read(20))
chat_id, = struct.unpack("<q", buffer.read(8))
chat_access_hash = struct.unpack("<q", buffer.read(8))[0] if bf_len - buffer.tell() >= 8 else None

return FileId(
fileId = FileId(
major=major,
minor=minor,
file_type=file_type,
Expand All @@ -305,26 +309,27 @@ def decode(file_id: str):
thumbnail_source=thumbnail_source,
chat_id=chat_id,
chat_access_hash=chat_access_hash,
local_id=local_id
)

if thumbnail_source == ThumbnailSource.STICKER_SET_THUMBNAIL:
sticker_set_id, sticker_set_access_hash, local_id = struct.unpack("<qqi", buffer.read(20))
chat_id, chat_access_hash = struct.unpack("<qq", buffer.read(16))

return FileId(
fileId = FileId(
major=major,
minor=minor,
file_type=file_type,
dc_id=dc_id,
file_reference=file_reference,
media_id=media_id,
access_hash=access_hash,
volume_id=volume_id,
thumbnail_source=thumbnail_source,
sticker_set_id=sticker_set_id,
sticker_set_access_hash=sticker_set_access_hash,
local_id=local_id
volume_id=volume_id
)
fileId.local_id = struct.unpack('<i', buffer.read(4))[0]\
if thumbnail_source == ThumbnailSource.FULL_LEGACY or 22 <= minor < 32 else None
return fileId

if file_type in DOCUMENT_TYPES:
return FileId(
Expand Down
116 changes: 88 additions & 28 deletions pyrogram/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,56 +172,76 @@ async def all_filter(_, __, ___):
# endregion

# region me_filter
async def me_filter(_, __, m: Message):
async def me_filter(_, __, upd: Update):
if isinstance(upd, Message):
m = upd
elif not (m := upd.message):
raise ValueError(f"Me filter doesn't work with {type(upd)}")
return bool(m.from_user and m.from_user.is_self or getattr(m, "outgoing", False))


me = create(me_filter)
"""Filter messages generated by you yourself."""
"""Filter updates generated by you yourself."""


# endregion

# region bot_filter
async def bot_filter(_, __, m: Message):
async def bot_filter(_, __, upd: Update):
if isinstance(upd, Message):
m = upd
elif not (m := upd.message):
raise ValueError(f"Bot filter doesn't work with {type(upd)}")
return bool(m.from_user and m.from_user.is_bot)


bot = create(bot_filter)
"""Filter messages coming from bots."""
"""Filter updates coming from bots."""


# endregion

# region sender_chat_filter
async def sender_chat_filter(_, __, m: Message):
async def sender_chat_filter(_, __, upd: Update):
if isinstance(upd, Message):
m = upd
elif not (m := upd.message):
raise ValueError(f"Sender chat filter doesn't work with {type(upd)}")
return bool(m.sender_chat)


sender_chat = create(sender_chat_filter)
"""Filter messages coming from sender chat."""
"""Filter updates coming from sender chat."""


# endregion

# region incoming_filter
async def incoming_filter(_, __, m: Message):
async def incoming_filter(_, __, upd: Update):
if isinstance(upd, Message):
m = upd
elif not (m := upd.message):
raise ValueError(f"Incoming filter doesn't work with {type(upd)}")
return not m.outgoing


incoming = create(incoming_filter)
"""Filter incoming messages. Messages sent to your own chat (Saved Messages) are also recognised as incoming."""
"""Filter incoming updates. Updates sent to your own chat (Saved Messages) are also recognised as incoming."""


# endregion

# region outgoing_filter
async def outgoing_filter(_, __, m: Message):
async def outgoing_filter(_, __, upd: Update):
if isinstance(upd, Message):
m = upd
elif not (m := upd.message):
raise ValueError(f"Outgoing filter doesn't work with {type(upd)}")
return m.outgoing


outgoing = create(outgoing_filter)
"""Filter outgoing messages. Messages sent to your own chat (Saved Messages) are not recognized as outgoing."""
"""Filter outgoing updates. Updates sent to your own chat (Saved Messages) are not recognized as outgoing."""


# endregion
Expand Down Expand Up @@ -557,34 +577,46 @@ async def media_spoiler_filter(_, __, m: Message):
# endregion

# region private_filter
async def private_filter(_, __, m: Message):
async def private_filter(_, __, upd: Update):
if isinstance(upd, Message):
m = upd
elif not (m := upd.message):
raise ValueError(f"Private filter doesn't work with {type(upd)}")
return bool(m.chat and m.chat.type in {enums.ChatType.PRIVATE, enums.ChatType.BOT})


private = create(private_filter)
"""Filter messages sent in private chats."""
"""Filter updates sent in private chats."""


# endregion

# region group_filter
async def group_filter(_, __, m: Message):
async def group_filter(_, __, upd: Update):
if isinstance(upd, Message):
m = upd
elif not (m := upd.message):
raise ValueError(f"Group filter doesn't work with {type(upd)}")
return bool(m.chat and m.chat.type in {enums.ChatType.GROUP, enums.ChatType.SUPERGROUP, enums.ChatType.FORUM})


group = create(group_filter)
"""Filter messages sent in group or supergroup chats."""
"""Filter updates sent in group or supergroup chats."""


# endregion

# region channel_filter
async def channel_filter(_, __, m: Message):
async def channel_filter(_, __, upd: Update):
if isinstance(upd, Message):
m = upd
elif not (m := upd.message):
raise ValueError(f"Channel filter doesn't work with {type(upd)}")
return bool(m.chat and m.chat.type == enums.ChatType.CHANNEL)


channel = create(channel_filter)
"""Filter messages sent in channels."""
"""Filter updates sent in channels."""


# endregion
Expand All @@ -601,12 +633,16 @@ async def direct_filter(_, __, m: Message):
# endregion

# region forum_filter
async def forum_filter(_, __, m: Message):
async def forum_filter(_, __, upd: Update):
if isinstance(upd, Message):
m = upd
elif not (m := upd.message):
raise ValueError(f"Forum filter doesn't work with {type(upd)}")
return bool(m.chat and m.chat.is_forum)


forum = create(forum_filter)
"""Filter messages sent in forums."""
"""Filter updates sent in forums."""


# endregion
Expand Down Expand Up @@ -799,7 +835,11 @@ async def via_bot_filter(_, __, m: Message):
# endregion

# region admin_filter
async def admin_filter(_, __, m: Message):
async def admin_filter(_, __, upd: Update):
if isinstance(upd, Message):
m = upd
elif not (m := upd.message):
raise ValueError(f"Admin filter doesn't work with {type(upd)}")
return bool(m.chat and m.chat.is_admin)


Expand Down Expand Up @@ -832,12 +872,16 @@ async def video_chat_ended_filter(_, __, m: Message):
# endregion

# region business
async def business_filter(_, __, m: Message):
async def business_filter(_, __, upd: Update):
if isinstance(upd, Message):
m = upd
elif not (m := upd.message):
raise ValueError(f"Business filter doesn't work with {type(upd)}")
return bool(m.business_connection_id)


business = create(business_filter)
"""Filter messages sent via business bot"""
"""Filter updates sent via business bot"""


# endregion
Expand Down Expand Up @@ -930,7 +974,11 @@ async def paid_message_filter(_, __, m: Message):
# endregion

# region linked_channel_filter
async def linked_channel_filter(_, __, m: Message):
async def linked_channel_filter(_, __, upd: Update):
if isinstance(upd, Message):
m = upd
elif not (m := upd.message):
raise ValueError(f"LinkedChannel filter doesn't work with {type(upd)}")
return bool(
m.forward_origin and
m.forward_origin.type == enums.MessageOriginType.CHANNEL and
Expand Down Expand Up @@ -1107,7 +1155,7 @@ async def func(flt, _, update: Update):

# noinspection PyPep8Naming
class user(Filter, set):
"""Filter messages coming from one or more users.
"""Filter updates coming from one or more users.

You can use `set bound methods <https://docs.python.org/3/library/stdtypes.html#set>`_ to manipulate the
users container.
Expand All @@ -1128,7 +1176,11 @@ def __init__(self, users: Optional[Union[int, str, List[Union[int, str]]]] = Non
else u for u in users
)

async def __call__(self, _, message: Message):
async def __call__(self, _, update: Update):
if isinstance(update, Message):
message = update
elif not (message := update.message):
raise ValueError(f"User filter doesn't work with {type(update)}")
return (message.from_user
and (message.from_user.id in self
or (message.from_user.username
Expand All @@ -1139,7 +1191,7 @@ async def __call__(self, _, message: Message):

# noinspection PyPep8Naming
class chat(Filter, set):
"""Filter messages coming from one or more chats.
"""Filter updates coming from one or more chats.

You can use `set bound methods <https://docs.python.org/3/library/stdtypes.html#set>`_ to manipulate the
chats container.
Expand All @@ -1160,7 +1212,11 @@ def __init__(self, chats: Optional[Union[int, str, List[Union[int, str]]]] = Non
else c for c in chats
)

async def __call__(self, _, message: Message):
async def __call__(self, _, update: Update):
if isinstance(update, Message):
message = update
elif not (message := update.message):
raise ValueError(f"Chat filter doesn't work with {type(update)}")
return (message.chat
and (message.chat.id in self
or (message.chat.username
Expand All @@ -1173,7 +1229,7 @@ async def __call__(self, _, message: Message):

# noinspection PyPep8Naming
class topic(Filter, set):
"""Filter messages coming from one or more topics.
"""Filter updates coming from one or more topics.

You can use `set bound methods <https://docs.python.org/3/library/stdtypes.html#set>`_ to manipulate the
topics container.
Expand All @@ -1191,5 +1247,9 @@ def __init__(self, topics: Optional[Union[int, List[int]]] = None):
t for t in topics
)

async def __call__(self, _, message: Message):
async def __call__(self, _, update: Update):
if isinstance(update, Message):
message = update
elif not (message := update.message):
raise ValueError(f"Topic filter doesn't work with {type(update)}")
return message.topic and message.topic.id in self