mirror of
				https://github.com/LonamiWebs/Telethon.git
				synced 2025-10-30 23:47:33 +03:00 
			
		
		
		
	Handle all entity types on isinstance checks
Only the uses of `isinstance` against `InputPeer*` types were reviewed. Notably, `utils` is exempt on this because it needs to deal with everything on a case-by-case basis. Since the addition of `*FromMessage` peers, any manual `isinstance` checks to determine the type were prone to breaking or being forgotten to be updated, so a common `helpers._entity_type()` method was made to share this logic. Since the conversion to `Peer` would be too expensive, a simpler check against the name is made, which should be fast and cheap.
This commit is contained in:
		
							parent
							
								
									627e176f8e
								
							
						
					
					
						commit
						fa736f81af
					
				|  | @ -108,8 +108,8 @@ class _ParticipantsIter(RequestIter): | |||
|                 filter = filter() | ||||
| 
 | ||||
|         entity = await self.client.get_input_entity(entity) | ||||
|         if search and (filter | ||||
|                        or not isinstance(entity, types.InputPeerChannel)): | ||||
|         ty = helpers._entity_type(entity) | ||||
|         if search and (filter or ty != helpers._EntityType.CHANNEL): | ||||
|             # We need to 'search' ourselves unless we have a PeerChannel | ||||
|             search = search.casefold() | ||||
| 
 | ||||
|  | @ -123,7 +123,7 @@ class _ParticipantsIter(RequestIter): | |||
|         # Only used for channels, but we should always set the attribute | ||||
|         self.requests = [] | ||||
| 
 | ||||
|         if isinstance(entity, types.InputPeerChannel): | ||||
|         if ty == helpers._EntityType.CHANNEL: | ||||
|             self.total = (await self.client( | ||||
|                 functions.channels.GetFullChannelRequest(entity) | ||||
|             )).full_chat.participants_count | ||||
|  | @ -149,7 +149,7 @@ class _ParticipantsIter(RequestIter): | |||
|                     hash=0 | ||||
|                 )) | ||||
| 
 | ||||
|         elif isinstance(entity, types.InputPeerChat): | ||||
|         elif ty == helpers._EntityType.CHAT: | ||||
|             full = await self.client( | ||||
|                 functions.messages.GetFullChatRequest(entity.chat_id)) | ||||
|             if not isinstance( | ||||
|  | @ -281,7 +281,8 @@ class _ProfilePhotoIter(RequestIter): | |||
|             self, entity, offset, max_id | ||||
|     ): | ||||
|         entity = await self.client.get_input_entity(entity) | ||||
|         if isinstance(entity, (types.InputPeerUser, types.InputPeerSelf)): | ||||
|         ty = helpers._entity_type(entity) | ||||
|         if ty == helpers._EntityType.USER: | ||||
|             self.request = functions.photos.GetUserPhotosRequest( | ||||
|                 entity, | ||||
|                 offset=offset, | ||||
|  | @ -864,7 +865,8 @@ class ChatMethods: | |||
|         """ | ||||
|         entity = await self.get_input_entity(entity) | ||||
|         user = await self.get_input_entity(user) | ||||
|         if not isinstance(user, (types.InputPeerUser, types.InputPeerSelf)): | ||||
|         ty = helpers._entity_type(user) | ||||
|         if ty != helpers._EntityType.USER: | ||||
|             raise ValueError('You must pass a user entity') | ||||
| 
 | ||||
|         perm_names = ( | ||||
|  | @ -872,7 +874,8 @@ class ChatMethods: | |||
|             'ban_users', 'invite_users', 'pin_messages', 'add_admins' | ||||
|         ) | ||||
| 
 | ||||
|         if isinstance(entity, types.InputPeerChannel): | ||||
|         ty = helpers._entity_type(entity) | ||||
|         if ty == helpers._EntityType.CHANNEL: | ||||
|             # If we try to set these permissions in a megagroup, we | ||||
|             # would get a RIGHT_FORBIDDEN. However, it makes sense | ||||
|             # that an admin can post messages, so we want to avoid the error | ||||
|  | @ -894,7 +897,7 @@ class ChatMethods: | |||
|                 for name in perm_names | ||||
|             }), rank=title or '')) | ||||
| 
 | ||||
|         elif isinstance(entity, types.InputPeerChat): | ||||
|         elif ty == helpers._EntityType.CHAT: | ||||
|             # If the user passed any permission in a small | ||||
|             # group chat, they must be a full admin to have it. | ||||
|             if is_admin is None: | ||||
|  | @ -1015,7 +1018,8 @@ class ChatMethods: | |||
|                 await client.edit_permissions(chat, user) | ||||
|         """ | ||||
|         entity = await self.get_input_entity(entity) | ||||
|         if not isinstance(entity, types.InputPeerChannel): | ||||
|         ty = helpers._entity_type(entity) | ||||
|         if ty != helpers._EntityType.CHANNEL: | ||||
|             raise ValueError('You must pass either a channel or a supergroup') | ||||
| 
 | ||||
|         rights = types.ChatBannedRights( | ||||
|  | @ -1040,12 +1044,13 @@ class ChatMethods: | |||
|             )) | ||||
| 
 | ||||
|         user = await self.get_input_entity(user) | ||||
|         ty = helpers._entity_type(user) | ||||
|         if ty != helpers._EntityType.USER: | ||||
|             raise ValueError('You must pass a user entity') | ||||
| 
 | ||||
|         if isinstance(user, types.InputPeerSelf): | ||||
|             raise ValueError('You cannot restrict yourself') | ||||
| 
 | ||||
|         if not isinstance(user, types.InputPeerUser): | ||||
|             raise ValueError('You must pass a user entity') | ||||
| 
 | ||||
|         return await self(functions.channels.EditBannedRequest( | ||||
|             channel=entity, | ||||
|             user_id=user, | ||||
|  | @ -1086,12 +1091,13 @@ class ChatMethods: | |||
|         """ | ||||
|         entity = await self.get_input_entity(entity) | ||||
|         user = await self.get_input_entity(user) | ||||
|         if not isinstance(user, (types.InputPeerUser, types.InputPeerSelf)): | ||||
|         if helpers._entity_type(user) != helpers._EntityType.USER: | ||||
|             raise ValueError('You must pass a user entity') | ||||
| 
 | ||||
|         if isinstance(entity, types.InputPeerChat): | ||||
|         ty = helpers._entity_type(entity) | ||||
|         if ty == helpers._EntityType.CHAT: | ||||
|             await self(functions.messages.DeleteChatUserRequest(entity.chat_id, user)) | ||||
|         elif isinstance(entity, types.InputPeerChannel): | ||||
|         elif ty == helpers._EntityType.CHANNEL: | ||||
|             if isinstance(user, types.InputPeerSelf): | ||||
|                 await self(functions.channels.LeaveChannelRequest(entity)) | ||||
|             else: | ||||
|  |  | |||
|  | @ -3,7 +3,7 @@ import inspect | |||
| import itertools | ||||
| import typing | ||||
| 
 | ||||
| from .. import utils, hints | ||||
| from .. import helpers, utils, hints | ||||
| from ..requestiter import RequestIter | ||||
| from ..tl import types, functions, custom | ||||
| 
 | ||||
|  | @ -436,10 +436,11 @@ class DialogMethods: | |||
|                 await client.delete_dialog('username') | ||||
|         """ | ||||
|         entity = await self.get_input_entity(entity) | ||||
|         if isinstance(entity, types.InputPeerChannel): | ||||
|         ty = helpers._entity_type(entity) | ||||
|         if ty == helpers._EntityType.CHANNEL: | ||||
|             return await self(functions.channels.LeaveChannelRequest(entity)) | ||||
| 
 | ||||
|         if isinstance(entity, types.InputPeerChat): | ||||
|         if ty == helpers._EntityType.CHAT: | ||||
|             result = await self(functions.messages.DeleteChatUserRequest( | ||||
|                 entity.chat_id, types.InputUserSelf())) | ||||
|         else: | ||||
|  |  | |||
|  | @ -257,7 +257,8 @@ class DownloadMethods: | |||
|             # See issue #500, Android app fails as of v4.6.0 (1155). | ||||
|             # The fix seems to be using the full channel chat photo. | ||||
|             ie = await self.get_input_entity(entity) | ||||
|             if isinstance(ie, types.InputPeerChannel): | ||||
|             ty = helpers._entity_type(ie) | ||||
|             if ty == helpers._EntityType.CHANNEL: | ||||
|                 full = await self(functions.channels.GetFullChannelRequest(ie)) | ||||
|                 return await self._download_photo( | ||||
|                     full.full_chat.chat_photo, file, | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ import itertools | |||
| import re | ||||
| import typing | ||||
| 
 | ||||
| from .. import utils | ||||
| from .. import helpers, utils | ||||
| from ..tl import types | ||||
| 
 | ||||
| if typing.TYPE_CHECKING: | ||||
|  | @ -134,7 +134,7 @@ class MessageParseMethods: | |||
|                 id_to_message[update.message.id] = update.message | ||||
| 
 | ||||
|             elif (isinstance(update, types.UpdateEditMessage) | ||||
|                   and not isinstance(request.peer, types.InputPeerChannel)): | ||||
|                   and helpers._entity_type(request.peer) != helpers._EntityType.CHANNEL): | ||||
|                 if request.id == update.message.id: | ||||
|                     update.message._finish_init(self, entities, input_chat) | ||||
|                     return update.message | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ import inspect | |||
| import itertools | ||||
| import typing | ||||
| 
 | ||||
| from .. import utils, errors, hints | ||||
| from .. import helpers, utils, errors, hints | ||||
| from ..requestiter import RequestIter | ||||
| from ..tl import types, functions | ||||
| 
 | ||||
|  | @ -57,8 +57,8 @@ class _MessagesIter(RequestIter): | |||
| 
 | ||||
|         if from_user: | ||||
|             from_user = await self.client.get_input_entity(from_user) | ||||
|             if not isinstance(from_user, ( | ||||
|                     types.InputPeerUser, types.InputPeerSelf)): | ||||
|             ty = helpers._entity_type(from_user) | ||||
|             if ty != helpers._EntityType.USER: | ||||
|                 from_user = None  # Ignore from_user unless it's a user | ||||
| 
 | ||||
|         if from_user: | ||||
|  | @ -86,8 +86,8 @@ class _MessagesIter(RequestIter): | |||
|                 filter = types.InputMessagesFilterEmpty() | ||||
| 
 | ||||
|             # Telegram completely ignores `from_id` in private chats | ||||
|             if isinstance( | ||||
|                     self.entity, (types.InputPeerUser, types.InputPeerSelf)): | ||||
|             ty = helpers._entity_type(self.entity) | ||||
|             if ty == helpers._EntityType.USER: | ||||
|                 # Don't bother sending `from_user` (it's ignored anyway), | ||||
|                 # but keep `from_id` defined above to check it locally. | ||||
|                 from_user = None | ||||
|  | @ -246,6 +246,7 @@ class _IDsIter(RequestIter): | |||
|         self._ids = list(reversed(ids)) if self.reverse else ids | ||||
|         self._offset = 0 | ||||
|         self._entity = (await self.client.get_input_entity(entity)) if entity else None | ||||
|         self._ty = helpers._EntityType(self._entity) if self._entity else None | ||||
| 
 | ||||
|         # 30s flood wait every 300 messages (3 requests of 100 each, 30 of 10, etc.) | ||||
|         if self.wait_time is None: | ||||
|  | @ -259,7 +260,7 @@ class _IDsIter(RequestIter): | |||
|         self._offset += _MAX_CHUNK_SIZE | ||||
| 
 | ||||
|         from_id = None  # By default, no need to validate from_id | ||||
|         if isinstance(self._entity, (types.InputChannel, types.InputPeerChannel)): | ||||
|         if self._ty == helpers._EntityType.CHANNEL: | ||||
|             try: | ||||
|                 r = await self.client( | ||||
|                     functions.channels.GetMessagesRequest(self._entity, ids)) | ||||
|  | @ -1108,7 +1109,7 @@ class MessageMethods: | |||
|         ) | ||||
| 
 | ||||
|         entity = await self.get_input_entity(entity) if entity else None | ||||
|         if isinstance(entity, types.InputPeerChannel): | ||||
|         if helpers._entity_type(entity) == helpers._EntityType.CHANNEL: | ||||
|             return await self([functions.channels.DeleteMessagesRequest( | ||||
|                          entity, list(c)) for c in utils.chunks(message_ids)]) | ||||
|         else: | ||||
|  | @ -1181,7 +1182,7 @@ class MessageMethods: | |||
|                 return True | ||||
| 
 | ||||
|         if max_id is not None: | ||||
|             if isinstance(entity, types.InputPeerChannel): | ||||
|             if helpers._entity_type(entity) == helpers._EntityType.CHANNEL: | ||||
|                 return await self(functions.channels.ReadHistoryRequest( | ||||
|                     utils.get_input_channel(entity), max_id=max_id)) | ||||
|             else: | ||||
|  |  | |||
|  | @ -4,7 +4,7 @@ import itertools | |||
| import time | ||||
| import typing | ||||
| 
 | ||||
| from .. import errors, utils, hints | ||||
| from .. import errors, helpers, utils, hints | ||||
| from ..errors import MultiError, RPCError | ||||
| from ..helpers import retry_range | ||||
| from ..tl import TLRequest, types, functions | ||||
|  | @ -258,12 +258,20 @@ class UserMethods: | |||
|             else: | ||||
|                 inputs.append(await self.get_input_entity(x)) | ||||
| 
 | ||||
|         users = [x for x in inputs | ||||
|                  if isinstance(x, (types.InputPeerUser, types.InputPeerSelf))] | ||||
|         chats = [x.chat_id for x in inputs | ||||
|                  if isinstance(x, types.InputPeerChat)] | ||||
|         channels = [x for x in inputs | ||||
|                     if isinstance(x, types.InputPeerChannel)] | ||||
|         lists = { | ||||
|             helpers._EntityType.USER: [], | ||||
|             helpers._EntityType.CHAT: [], | ||||
|             helpers._EntityType.CHANNEL: [], | ||||
|         } | ||||
|         for x in inputs: | ||||
|             try: | ||||
|                 lists[helpers._entity_type(x)].append(x) | ||||
|             except TypeError: | ||||
|                 pass | ||||
| 
 | ||||
|         users = lists[helpers._EntityType.USER] | ||||
|         chats = lists[helpers._EntityType.CHAT] | ||||
|         channels = lists[helpers._EntityType.CHANNEL] | ||||
|         if users: | ||||
|             # GetUsersRequest has a limit of 200 per call | ||||
|             tmp = [] | ||||
|  |  | |||
|  | @ -1,10 +1,17 @@ | |||
| """Various helpers not related to the Telegram API itself""" | ||||
| import asyncio | ||||
| import enum | ||||
| import os | ||||
| import struct | ||||
| from hashlib import sha1 | ||||
| 
 | ||||
| 
 | ||||
| class _EntityType(enum.Enum): | ||||
|     USER = 0 | ||||
|     CHAT = 1 | ||||
|     CHANNEL = 2 | ||||
| 
 | ||||
| 
 | ||||
| # region Multiple utilities | ||||
| 
 | ||||
| 
 | ||||
|  | @ -131,6 +138,41 @@ def _sync_exit(self, *args): | |||
|     return loop.run_until_complete(self.__aexit__(*args)) | ||||
| 
 | ||||
| 
 | ||||
| def _entity_type(entity): | ||||
|     # This could be a `utils` method that just ran a few `isinstance` on | ||||
|     # `utils.get_peer(...)`'s result. However, there are *a lot* of auto | ||||
|     # casts going on, plenty of calls and temporary short-lived objects. | ||||
|     # | ||||
|     # So we just check if a string is in the class name. | ||||
|     # Still, assert that it's the right type to not return false results. | ||||
|     try: | ||||
|         if entity.SUBCLASS_OF_ID not in ( | ||||
|                 0x2d45687,  # crc32(b'Peer') | ||||
|                 0xc91c90b6,  # crc32(b'InputPeer') | ||||
|                 0xe669bf46,  # crc32(b'InputUser') | ||||
|                 0x40f202fd,  # crc32(b'InputChannel') | ||||
|                 0x2da17977,  # crc32(b'User') | ||||
|                 0xc5af5d94,  # crc32(b'Chat') | ||||
|                 0x1f4661b9,  # crc32(b'UserFull') | ||||
|                 0xd49a2697,  # crc32(b'ChatFull') | ||||
|         ): | ||||
|             raise TypeError('{} does not have any entity type'.format(entity)) | ||||
|     except AttributeError: | ||||
|         raise TypeError('{} is not a TLObject, cannot determine entity type'.format(entity)) | ||||
| 
 | ||||
|     name = entity.__class__.__name__ | ||||
|     if 'User' in name: | ||||
|         return _EntityType.USER | ||||
|     elif 'Chat' in name: | ||||
|         return _EntityType.CHAT | ||||
|     elif 'Channel' in name: | ||||
|         return _EntityType.CHANNEL | ||||
|     elif 'Self' in name: | ||||
|         return _EntityType.USER | ||||
| 
 | ||||
|     # 'Empty' in name or not found, we don't care, not a valid entity. | ||||
|     raise TypeError('{} does not have any entity type'.format(entity)) | ||||
| 
 | ||||
| # endregion | ||||
| 
 | ||||
| # region Cryptographic related utils | ||||
|  |  | |||
|  | @ -149,6 +149,7 @@ MESSAGE_IDS_EMPTY,400,No message ids were provided | |||
| MESSAGE_ID_INVALID,400,"The specified message ID is invalid or you can't do that operation on such message" | ||||
| MESSAGE_NOT_MODIFIED,400,Content of the message was not modified | ||||
| MESSAGE_TOO_LONG,400,Message was too long. Current maximum length is 4096 UTF-8 characters | ||||
| MSG_ID_INVALID,400,The message ID used in the peer was invalid | ||||
| MSG_WAIT_FAILED,400,A waiting call returned an error | ||||
| MT_SEND_QUEUE_TOO_LONG,500, | ||||
| NEED_CHAT_INVALID,500,The provided chat is invalid | ||||
|  |  | |||
| 
 | 
|  | @ -238,7 +238,7 @@ messages.sendEncryptedFile,user,MSG_WAIT_FAILED | |||
| messages.sendEncryptedService,user,DATA_INVALID ENCRYPTION_DECLINED MSG_WAIT_FAILED USER_IS_BLOCKED | ||||
| messages.sendInlineBotResult,user,CHAT_SEND_INLINE_FORBIDDEN CHAT_WRITE_FORBIDDEN INLINE_RESULT_EXPIRED PEER_ID_INVALID QUERY_ID_EMPTY SCHEDULE_DATE_TOO_LATE SCHEDULE_TOO_MUCH WEBPAGE_CURL_FAILED WEBPAGE_MEDIA_EMPTY | ||||
| messages.sendMedia,both,BOT_PAYMENTS_DISABLED BOT_POLLS_DISABLED CHANNEL_INVALID CHANNEL_PRIVATE CHAT_ADMIN_REQUIRED CHAT_SEND_MEDIA_FORBIDDEN CHAT_WRITE_FORBIDDEN EXTERNAL_URL_INVALID FILE_PARTS_INVALID FILE_PART_LENGTH_INVALID INPUT_USER_DEACTIVATED MEDIA_CAPTION_TOO_LONG MEDIA_EMPTY PAYMENT_PROVIDER_INVALID PEER_ID_INVALID PHOTO_EXT_INVALID PHOTO_INVALID_DIMENSIONS PHOTO_SAVE_FILE_INVALID POLL_OPTION_DUPLICATE RANDOM_ID_DUPLICATE SCHEDULE_DATE_TOO_LATE SCHEDULE_TOO_MUCH STORAGE_CHECK_FAILED Timeout USER_BANNED_IN_CHANNEL USER_IS_BLOCKED USER_IS_BOT VIDEO_CONTENT_TYPE_INVALID WEBPAGE_CURL_FAILED WEBPAGE_MEDIA_EMPTY | ||||
| messages.sendMessage,both,AUTH_KEY_DUPLICATED BUTTON_DATA_INVALID BUTTON_TYPE_INVALID BUTTON_URL_INVALID CHANNEL_INVALID CHANNEL_PRIVATE CHAT_ADMIN_REQUIRED CHAT_ID_INVALID CHAT_RESTRICTED CHAT_WRITE_FORBIDDEN ENTITIES_TOO_LONG ENTITY_MENTION_USER_INVALID INPUT_USER_DEACTIVATED MESSAGE_EMPTY MESSAGE_TOO_LONG PEER_ID_INVALID RANDOM_ID_DUPLICATE REPLY_MARKUP_INVALID REPLY_MARKUP_TOO_LONG SCHEDULE_BOT_NOT_ALLOWED SCHEDULE_DATE_TOO_LATE SCHEDULE_TOO_MUCH Timeout USER_BANNED_IN_CHANNEL USER_IS_BLOCKED USER_IS_BOT YOU_BLOCKED_USER | ||||
| messages.sendMessage,both,AUTH_KEY_DUPLICATED BUTTON_DATA_INVALID BUTTON_TYPE_INVALID BUTTON_URL_INVALID CHANNEL_INVALID CHANNEL_PRIVATE CHAT_ADMIN_REQUIRED CHAT_ID_INVALID CHAT_RESTRICTED CHAT_WRITE_FORBIDDEN ENTITIES_TOO_LONG ENTITY_MENTION_USER_INVALID INPUT_USER_DEACTIVATED MESSAGE_EMPTY MESSAGE_TOO_LONG MSG_ID_INVALID PEER_ID_INVALID RANDOM_ID_DUPLICATE REPLY_MARKUP_INVALID REPLY_MARKUP_TOO_LONG SCHEDULE_BOT_NOT_ALLOWED SCHEDULE_DATE_TOO_LATE SCHEDULE_TOO_MUCH Timeout USER_BANNED_IN_CHANNEL USER_IS_BLOCKED USER_IS_BOT YOU_BLOCKED_USER | ||||
| messages.sendMultiMedia,both,SCHEDULE_DATE_TOO_LATE SCHEDULE_TOO_MUCH | ||||
| messages.sendReaction,User,REACTION_INVALID | ||||
| messages.sendVote,user, | ||||
|  |  | |||
| 
 | 
		Loading…
	
		Reference in New Issue
	
	Block a user