From 83f2111be95e4e52d5b0df7146468bbed7318fb7 Mon Sep 17 00:00:00 2001 From: NickolaiH <48893302+WubbaLubbaDubDubDev@users.noreply.github.com> Date: Tue, 19 Aug 2025 18:30:56 +0300 Subject: [PATCH 1/9] FIX get_dialogs.py Fix infinite loop in get_dialogs() by preventing repeated dialogs - Added tracking of seen dialog IDs to avoid yielding duplicates - Added check for None top_message to safely stop pagination - Preserved support for limit, pinned_only, and chat_list parameters - Ensured correct offset updates for reliable pagination --- pyrogram/methods/chats/get_dialogs.py | 60 +++++++++------------------ 1 file changed, 19 insertions(+), 41 deletions(-) diff --git a/pyrogram/methods/chats/get_dialogs.py b/pyrogram/methods/chats/get_dialogs.py index cfd8c145a3..660637d5b7 100644 --- a/pyrogram/methods/chats/get_dialogs.py +++ b/pyrogram/methods/chats/get_dialogs.py @@ -16,11 +16,10 @@ # You should have received a copy of the GNU Lesser General Public License # along with Pyrogram. If not, see . -from asyncio import sleep from typing import AsyncGenerator, Optional -import pyrogram from pyrogram import types, raw, utils +from pyrogram.errors import ChannelPrivate, PeerIdInvalid class GetDialogs: @@ -30,47 +29,24 @@ async def get_dialogs( pinned_only: bool = False, chat_list: int = 0 ) -> Optional[AsyncGenerator["types.Dialog", None]]: - """Get a user's dialogs sequentially. - - .. include:: /_includes/usable-by/users.rst - - Parameters: - limit (``int``, *optional*): - Limits the number of dialogs to be retrieved. - By default, no limit is applied and all dialogs are returned. - - pinned_only (``bool``, *optional*): - Pass True if you want to get only pinned dialogs. - Defaults to False. - - chat_list (``int``, *optional*): - Chat list from which to get the dialogs; Only Main (0) and Archive (1) chat lists are supported. Defaults to (0) Main chat list. - - Returns: - ``Generator``: A generator yielding :obj:`~pyrogram.types.Dialog` objects. - - Example: - .. code-block:: python - - # Iterate through all dialogs - async for dialog in app.get_dialogs(): - print(dialog.chat.first_name or dialog.chat.title) - """ + current = 0 total = limit or (1 << 31) - 1 - limit = min(100, total) + request_limit = min(100, total) offset_date = 0 offset_id = 0 offset_peer = raw.types.InputPeerEmpty() + seen_dialog_ids = set() + while True: r = await self.invoke( raw.functions.messages.GetDialogs( offset_date=offset_date, offset_id=offset_id, offset_peer=offset_peer, - limit=limit, + limit=request_limit, hash=0, exclude_pinned=not pinned_only, folder_id=chat_list @@ -88,13 +64,10 @@ async def get_dialogs( continue chat_id = utils.get_peer_id(message.peer_id) - messages[chat_id] = await types.Message._parse( - self, - message, - users, - chats, - replies=self.fetch_replies - ) + try: + messages[chat_id] = await types.Message._parse(self, message, users, chats) + except (ChannelPrivate, PeerIdInvalid): + continue dialogs = [] @@ -102,22 +75,27 @@ async def get_dialogs( if not isinstance(dialog, raw.types.Dialog): continue - dialogs.append(types.Dialog._parse(self, dialog, messages, users, chats)) + parsed = types.Dialog._parse(self, dialog, messages, users, chats) + if parsed.chat.id in seen_dialog_ids: + continue + seen_dialog_ids.add(parsed.chat.id) + + dialogs.append(parsed) if not dialogs: return last = dialogs[-1] + if last.top_message is None: + return + offset_id = last.top_message.id offset_date = utils.datetime_to_timestamp(last.top_message.date) offset_peer = await self.resolve_peer(last.chat.id) for dialog in dialogs: - await sleep(0) yield dialog - current += 1 - if current >= total: return From 6ebe425edf79f955c2b2695c0efff11c85b9e6e6 Mon Sep 17 00:00:00 2001 From: NickolaiH <48893302+WubbaLubbaDubDubDev@users.noreply.github.com> Date: Tue, 19 Aug 2025 18:59:59 +0300 Subject: [PATCH 2/9] FIX get_dialogs.py fix: skip dialogs with no parsed chat to avoid errors --- pyrogram/methods/chats/get_dialogs.py | 35 +++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/pyrogram/methods/chats/get_dialogs.py b/pyrogram/methods/chats/get_dialogs.py index 660637d5b7..89a1f3b0bb 100644 --- a/pyrogram/methods/chats/get_dialogs.py +++ b/pyrogram/methods/chats/get_dialogs.py @@ -29,7 +29,32 @@ async def get_dialogs( pinned_only: bool = False, chat_list: int = 0 ) -> Optional[AsyncGenerator["types.Dialog", None]]: - + """Get a user's dialogs sequentially. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + limit (``int``, *optional*): + Limits the number of dialogs to be retrieved. + By default, no limit is applied and all dialogs are returned. + + pinned_only (``bool``, *optional*): + Pass True if you want to get only pinned dialogs. + Defaults to False. + + chat_list (``int``, *optional*): + Chat list from which to get the dialogs; Only Main (0) and Archive (1) chat lists are supported. Defaults to (0) Main chat list. + + Returns: + ``Generator``: A generator yielding :obj:`~pyrogram.types.Dialog` objects. + + Example: + .. code-block:: python + + # Iterate through all dialogs + async for dialog in app.get_dialogs(): + print(dialog.chat.first_name or dialog.chat.title) + """ current = 0 total = limit or (1 << 31) - 1 request_limit = min(100, total) @@ -76,10 +101,16 @@ async def get_dialogs( continue parsed = types.Dialog._parse(self, dialog, messages, users, chats) + if parsed is None: + continue + + if parsed.chat is None: + continue + if parsed.chat.id in seen_dialog_ids: continue + seen_dialog_ids.add(parsed.chat.id) - dialogs.append(parsed) if not dialogs: From 7c5883360baef2eb462c0d752b7f3bb1d8084375 Mon Sep 17 00:00:00 2001 From: Shrimadhav U K Date: Wed, 20 Aug 2025 09:16:54 +0530 Subject: [PATCH 3/9] Update get_dialogs.py --- pyrogram/methods/chats/get_dialogs.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/pyrogram/methods/chats/get_dialogs.py b/pyrogram/methods/chats/get_dialogs.py index 89a1f3b0bb..40e1e173b9 100644 --- a/pyrogram/methods/chats/get_dialogs.py +++ b/pyrogram/methods/chats/get_dialogs.py @@ -16,10 +16,10 @@ # You should have received a copy of the GNU Lesser General Public License # along with Pyrogram. If not, see . +from asyncio import sleep from typing import AsyncGenerator, Optional -from pyrogram import types, raw, utils -from pyrogram.errors import ChannelPrivate, PeerIdInvalid +from pyrogram import raw, types, utils class GetDialogs: @@ -89,10 +89,7 @@ async def get_dialogs( continue chat_id = utils.get_peer_id(message.peer_id) - try: - messages[chat_id] = await types.Message._parse(self, message, users, chats) - except (ChannelPrivate, PeerIdInvalid): - continue + messages[chat_id] = await types.Message._parse(self, message, users, chats, replies=self.fetch_replies) dialogs = [] @@ -126,6 +123,7 @@ async def get_dialogs( offset_peer = await self.resolve_peer(last.chat.id) for dialog in dialogs: + await sleep(0) yield dialog current += 1 if current >= total: From 1414e4ceb19587ad3dad07cfd52923d2813bef3b Mon Sep 17 00:00:00 2001 From: Shrimadhav U K Date: Wed, 20 Aug 2025 09:17:21 +0530 Subject: [PATCH 4/9] Update get_dialogs.py --- pyrogram/methods/chats/get_dialogs.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pyrogram/methods/chats/get_dialogs.py b/pyrogram/methods/chats/get_dialogs.py index 40e1e173b9..f06913a4c9 100644 --- a/pyrogram/methods/chats/get_dialogs.py +++ b/pyrogram/methods/chats/get_dialogs.py @@ -19,6 +19,7 @@ from asyncio import sleep from typing import AsyncGenerator, Optional +import pyrogram from pyrogram import raw, types, utils From d54c5bd5a89028b662ecd2201eadfcab8986c496 Mon Sep 17 00:00:00 2001 From: Shrimadhav U K Date: Wed, 20 Aug 2025 09:18:05 +0530 Subject: [PATCH 5/9] Update get_dialogs.py --- pyrogram/methods/chats/get_dialogs.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/pyrogram/methods/chats/get_dialogs.py b/pyrogram/methods/chats/get_dialogs.py index f06913a4c9..3318dd2535 100644 --- a/pyrogram/methods/chats/get_dialogs.py +++ b/pyrogram/methods/chats/get_dialogs.py @@ -90,7 +90,13 @@ async def get_dialogs( continue chat_id = utils.get_peer_id(message.peer_id) - messages[chat_id] = await types.Message._parse(self, message, users, chats, replies=self.fetch_replies) + messages[chat_id] = await types.Message._parse( + self, + message, + users, + chats, + replies=self.fetch_replies + ) dialogs = [] From 4fab224c38f59462ac3c0433e64afe9c822d4fbc Mon Sep 17 00:00:00 2001 From: NickolaiH <48893302+WubbaLubbaDubDubDev@users.noreply.github.com> Date: Wed, 20 Aug 2025 20:38:22 +0300 Subject: [PATCH 6/9] Update chat_permissions.py add support for can_send_plain permission in chat permissions --- pyrogram/types/user_and_chats/chat_permissions.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pyrogram/types/user_and_chats/chat_permissions.py b/pyrogram/types/user_and_chats/chat_permissions.py index 07b655caf9..3c8473d73d 100644 --- a/pyrogram/types/user_and_chats/chat_permissions.py +++ b/pyrogram/types/user_and_chats/chat_permissions.py @@ -78,6 +78,7 @@ class ChatPermissions(Object): def __init__( self, *, + can_send_plain: bool = None, can_send_messages: bool = None, # Text, contacts, giveaways, giveaway winners, invoices, locations and venues can_send_audios: bool = None, can_send_documents: bool = None, @@ -96,6 +97,7 @@ def __init__( ): super().__init__(None) + self.can_send_plain = can_send_plain self.can_send_messages = can_send_messages self.can_send_audios = can_send_audios self.can_send_documents = can_send_documents @@ -114,6 +116,7 @@ def __init__( @staticmethod def _parse(denied_permissions: "raw.base.ChatBannedRights") -> "ChatPermissions": + can_send_plain = False can_send_messages = False can_send_audios = False can_send_documents = False @@ -131,6 +134,7 @@ def _parse(denied_permissions: "raw.base.ChatBannedRights") -> "ChatPermissions" can_send_media_messages = False if isinstance(denied_permissions, raw.types.ChatBannedRights): + can_send_plain = not denied_permissions.send_plain can_send_messages = not denied_permissions.send_messages can_send_polls = not denied_permissions.send_polls can_send_other_messages = any([ @@ -171,6 +175,7 @@ def _parse(denied_permissions: "raw.base.ChatBannedRights") -> "ChatPermissions" can_send_voice_notes = can_send_media_messages return ChatPermissions( + can_send_plain=can_send_plain, can_send_messages=can_send_messages, can_send_audios=can_send_audios, can_send_documents=can_send_documents, @@ -217,5 +222,6 @@ def write( send_videos=not permissions.can_send_videos,# TODO send_roundvideos=not permissions.can_send_video_notes,# TODO send_voices=not permissions.can_send_voice_notes,# TODO + send_plain=not permissions.can_send_plain# TODO # send_plain=# TODO ) From 34847aba54e1bed9e13b4914cd3416fbcbe22dd0 Mon Sep 17 00:00:00 2001 From: NickolaiH <48893302+WubbaLubbaDubDubDev@users.noreply.github.com> Date: Wed, 20 Aug 2025 21:09:19 +0300 Subject: [PATCH 7/9] Update chat_permissions.py docstring Updated the class docstring to document this new permission, ensuring clarity and consistency with the actual class parameters. --- pyrogram/types/user_and_chats/chat_permissions.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pyrogram/types/user_and_chats/chat_permissions.py b/pyrogram/types/user_and_chats/chat_permissions.py index 3c8473d73d..778f1ef470 100644 --- a/pyrogram/types/user_and_chats/chat_permissions.py +++ b/pyrogram/types/user_and_chats/chat_permissions.py @@ -28,6 +28,9 @@ class ChatPermissions(Object): can_send_messages (``bool``, *optional*): True, if the user is allowed to send text messages, contacts, giveaways, giveaway winners, invoices, locations and venues + can_send_plain (``bool``, *optional*): + True, if the user is allowed to send plain text messages + can_send_audios (``bool``, *optional*): True, if the user is allowed to send audios @@ -223,5 +226,4 @@ def write( send_roundvideos=not permissions.can_send_video_notes,# TODO send_voices=not permissions.can_send_voice_notes,# TODO send_plain=not permissions.can_send_plain# TODO - # send_plain=# TODO ) From e3171d51ac1f9d1060a3a3d9029f05dd1548a93d Mon Sep 17 00:00:00 2001 From: Shrimadhav U K Date: Thu, 28 Aug 2025 10:18:33 +0530 Subject: [PATCH 8/9] Update set_chat_permissions.py --- .../methods/chats/set_chat_permissions.py | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/pyrogram/methods/chats/set_chat_permissions.py b/pyrogram/methods/chats/set_chat_permissions.py index d8ec0cf02b..bdc2f0216b 100644 --- a/pyrogram/methods/chats/set_chat_permissions.py +++ b/pyrogram/methods/chats/set_chat_permissions.py @@ -54,12 +54,12 @@ async def set_chat_permissions( # Completely restrict chat await app.set_chat_permissions(chat_id, ChatPermissions()) - # Chat members can only send text messages and media messages + # Chat members can only send text messages and documents await app.set_chat_permissions( chat_id, ChatPermissions( can_send_messages=True, - can_send_media_messages=True + can_send_documents=True ) ) """ @@ -67,20 +67,7 @@ async def set_chat_permissions( r = await self.invoke( raw.functions.messages.EditChatDefaultBannedRights( peer=await self.resolve_peer(chat_id), - banned_rights=raw.types.ChatBannedRights( - until_date=0, - send_messages=not permissions.can_send_messages, - send_media=not permissions.can_send_media_messages, - send_stickers=not permissions.can_send_other_messages, - send_gifs=not permissions.can_send_other_messages, - send_games=not permissions.can_send_other_messages, - send_inline=not permissions.can_send_other_messages, - embed_links=not permissions.can_add_web_page_previews, - send_polls=not permissions.can_send_polls, - change_info=not permissions.can_change_info, - invite_users=not permissions.can_invite_users, - pin_messages=not permissions.can_pin_messages, - ) + banned_rights=permissions.write(False) ) ) From acb1ddf80931a3a2d90737138fa3ea26ddc4ebb2 Mon Sep 17 00:00:00 2001 From: Shrimadhav U K Date: Thu, 28 Aug 2025 10:18:52 +0530 Subject: [PATCH 9/9] Update chat_permissions.py --- .../types/user_and_chats/chat_permissions.py | 146 ++++++++++++------ 1 file changed, 99 insertions(+), 47 deletions(-) diff --git a/pyrogram/types/user_and_chats/chat_permissions.py b/pyrogram/types/user_and_chats/chat_permissions.py index 778f1ef470..bc0f779c0b 100644 --- a/pyrogram/types/user_and_chats/chat_permissions.py +++ b/pyrogram/types/user_and_chats/chat_permissions.py @@ -16,10 +16,13 @@ # You should have received a copy of the GNU Lesser General Public License # along with Pyrogram. If not, see . +import logging from datetime import datetime from pyrogram import raw, utils from ..object import Object +log = logging.getLogger(__name__) + class ChatPermissions(Object): """Describes actions that a non-administrator user is allowed to take in a chat. @@ -28,9 +31,6 @@ class ChatPermissions(Object): can_send_messages (``bool``, *optional*): True, if the user is allowed to send text messages, contacts, giveaways, giveaway winners, invoices, locations and venues - can_send_plain (``bool``, *optional*): - True, if the user is allowed to send plain text messages - can_send_audios (``bool``, *optional*): True, if the user is allowed to send audios @@ -73,15 +73,11 @@ class ChatPermissions(Object): True, if the user is allowed to create forum topics If omitted defaults to the value of can_pin_messages - can_send_media_messages (``bool``, *optional*): - True, if the user is allowed to send audios, documents, photos, videos, video notes and voice notes. - Implies *can_send_messages*. """ def __init__( self, *, - can_send_plain: bool = None, can_send_messages: bool = None, # Text, contacts, giveaways, giveaway winners, invoices, locations and venues can_send_audios: bool = None, can_send_documents: bool = None, @@ -96,11 +92,9 @@ def __init__( can_invite_users: bool = None, can_pin_messages: bool = None, can_manage_topics: bool = None, - can_send_media_messages: bool = None, # Audio files, documents, photos, videos, video notes and voice notes ): super().__init__(None) - self.can_send_plain = can_send_plain self.can_send_messages = can_send_messages self.can_send_audios = can_send_audios self.can_send_documents = can_send_documents @@ -115,11 +109,9 @@ def __init__( self.can_invite_users = can_invite_users self.can_pin_messages = can_pin_messages self.can_manage_topics = can_manage_topics - self.can_send_media_messages = can_send_media_messages @staticmethod def _parse(denied_permissions: "raw.base.ChatBannedRights") -> "ChatPermissions": - can_send_plain = False can_send_messages = False can_send_audios = False can_send_documents = False @@ -134,10 +126,8 @@ def _parse(denied_permissions: "raw.base.ChatBannedRights") -> "ChatPermissions" can_invite_users = False can_pin_messages = False can_manage_topics = False - can_send_media_messages = False if isinstance(denied_permissions, raw.types.ChatBannedRights): - can_send_plain = not denied_permissions.send_plain can_send_messages = not denied_permissions.send_messages can_send_polls = not denied_permissions.send_polls can_send_other_messages = any([ @@ -168,17 +158,29 @@ def _parse(denied_permissions: "raw.base.ChatBannedRights") -> "ChatPermissions" can_send_videos = not denied_permissions.send_videos can_send_video_notes = not denied_permissions.send_roundvideos can_send_voice_notes = not denied_permissions.send_voices + send_aidem = any([ + can_send_audios, + can_send_documents, + can_send_photos, + can_send_videos, + can_send_video_notes, + can_send_voice_notes, + ]) + if not send_aidem: + can_send_messages = not any([ + denied_permissions.send_messages, + denied_permissions.send_plain + ]) else: - can_send_media_messages = not denied_permissions.send_media - can_send_audios = can_send_media_messages - can_send_documents = can_send_media_messages - can_send_photos = can_send_media_messages - can_send_videos = can_send_media_messages - can_send_video_notes = can_send_media_messages - can_send_voice_notes = can_send_media_messages + send_media = not denied_permissions.send_media + can_send_audios = send_media + can_send_documents = send_media + can_send_photos = send_media + can_send_videos = send_media + can_send_video_notes = send_media + can_send_voice_notes = send_media return ChatPermissions( - can_send_plain=can_send_plain, can_send_messages=can_send_messages, can_send_audios=can_send_audios, can_send_documents=can_send_documents, @@ -193,7 +195,6 @@ def _parse(denied_permissions: "raw.base.ChatBannedRights") -> "ChatPermissions" can_invite_users=can_invite_users, can_pin_messages=can_pin_messages, can_manage_topics=can_manage_topics, - can_send_media_messages=can_send_media_messages ) def write( @@ -201,29 +202,80 @@ def write( use_independent_chat_permissions: bool, until_date: datetime = utils.zero_datetime() ) -> "raw.base.ChatBannedRights": - return raw.types.ChatBannedRights( - until_date=utils.datetime_to_timestamp(until_date), - send_messages=not permissions.can_send_messages, - send_media=not permissions.can_send_media_messages, - send_stickers=not permissions.can_send_other_messages, - send_gifs=not permissions.can_send_other_messages, - send_games=not permissions.can_send_other_messages, - send_inline=not permissions.can_send_other_messages, - embed_links=not permissions.can_add_web_page_previews, - send_polls=not permissions.can_send_polls, - change_info=not permissions.can_change_info, - invite_users=not permissions.can_invite_users, - pin_messages=not permissions.can_pin_messages, - manage_topics=( - permissions.can_manage_topics and - not permissions.can_manage_topics - ) or not permissions.can_pin_messages, - # view_messages=# TODO - send_audios=not permissions.can_send_audios,# TODO - send_docs=not permissions.can_send_documents,# TODO - send_photos=not permissions.can_send_photos,# TODO - send_videos=not permissions.can_send_videos,# TODO - send_roundvideos=not permissions.can_send_video_notes,# TODO - send_voices=not permissions.can_send_voice_notes,# TODO - send_plain=not permissions.can_send_plain# TODO + if use_independent_chat_permissions: + return raw.types.ChatBannedRights( + until_date=utils.datetime_to_timestamp(until_date), + send_messages=not permissions.can_send_messages, + send_media=False, + send_stickers=not permissions.can_send_other_messages, + send_gifs=not permissions.can_send_other_messages, + send_games=not permissions.can_send_other_messages, + send_inline=not permissions.can_send_other_messages, + embed_links=not permissions.can_add_web_page_previews, + send_polls=not permissions.can_send_polls, + change_info=not permissions.can_change_info, + invite_users=not permissions.can_invite_users, + pin_messages=not permissions.can_pin_messages, + manage_topics=( + permissions.can_manage_topics and + not permissions.can_manage_topics + ) or not permissions.can_pin_messages, + view_messages=False, + send_audios=not permissions.can_send_audios, + send_docs=not permissions.can_send_documents, + send_photos=not permissions.can_send_photos, + send_videos=not permissions.can_send_videos, + send_roundvideos=not permissions.can_send_video_notes, + send_voices=not permissions.can_send_voice_notes, + send_plain=not permissions.can_send_messages, + ) + else: + send_aidem = any([ + permissions.can_send_audios, + permissions.can_send_documents, + permissions.can_send_photos, + permissions.can_send_videos, + permissions.can_send_video_notes, + permissions.can_send_voice_notes, + ]) + return raw.types.ChatBannedRights( + until_date=utils.datetime_to_timestamp(until_date), + send_messages=False, + send_media=not send_aidem, + send_stickers=not permissions.can_send_other_messages, + send_gifs=not permissions.can_send_other_messages, + send_games=not permissions.can_send_other_messages, + send_inline=not permissions.can_send_other_messages, + embed_links=not permissions.can_add_web_page_previews, + send_polls=not permissions.can_send_polls, + change_info=not permissions.can_change_info, + invite_users=not permissions.can_invite_users, + pin_messages=not permissions.can_pin_messages, + manage_topics=( + permissions.can_manage_topics and + not permissions.can_manage_topics + ) or not permissions.can_pin_messages, + view_messages=False, + send_audios=not permissions.can_send_audios, + send_docs=not permissions.can_send_documents, + send_photos=not permissions.can_send_photos, + send_videos=not permissions.can_send_videos, + send_roundvideos=not permissions.can_send_video_notes, + send_voices=not permissions.can_send_voice_notes, + send_plain=not permissions.can_send_messages, + ) + + @property + def can_send_media_messages(permissions: "ChatPermissions"): + log.warning( + "can_send_media_messages property is deprecated. " + "Please use any of can_send_audios, can_send_documents, can_send_photos, can_send_videos, can_send_video_notes or can_send_voice_notes." ) + return any([ + permissions.can_send_audios, + permissions.can_send_documents, + permissions.can_send_photos, + permissions.can_send_videos, + permissions.can_send_video_notes, + permissions.can_send_voice_notes, + ])