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:
Lonami Exo 2019-12-23 13:52:07 +01:00
parent 627e176f8e
commit fa736f81af
9 changed files with 97 additions and 37 deletions

View File

@ -108,8 +108,8 @@ class _ParticipantsIter(RequestIter):
filter = filter() filter = filter()
entity = await self.client.get_input_entity(entity) entity = await self.client.get_input_entity(entity)
if search and (filter ty = helpers._entity_type(entity)
or not isinstance(entity, types.InputPeerChannel)): if search and (filter or ty != helpers._EntityType.CHANNEL):
# We need to 'search' ourselves unless we have a PeerChannel # We need to 'search' ourselves unless we have a PeerChannel
search = search.casefold() search = search.casefold()
@ -123,7 +123,7 @@ class _ParticipantsIter(RequestIter):
# Only used for channels, but we should always set the attribute # Only used for channels, but we should always set the attribute
self.requests = [] self.requests = []
if isinstance(entity, types.InputPeerChannel): if ty == helpers._EntityType.CHANNEL:
self.total = (await self.client( self.total = (await self.client(
functions.channels.GetFullChannelRequest(entity) functions.channels.GetFullChannelRequest(entity)
)).full_chat.participants_count )).full_chat.participants_count
@ -149,7 +149,7 @@ class _ParticipantsIter(RequestIter):
hash=0 hash=0
)) ))
elif isinstance(entity, types.InputPeerChat): elif ty == helpers._EntityType.CHAT:
full = await self.client( full = await self.client(
functions.messages.GetFullChatRequest(entity.chat_id)) functions.messages.GetFullChatRequest(entity.chat_id))
if not isinstance( if not isinstance(
@ -281,7 +281,8 @@ class _ProfilePhotoIter(RequestIter):
self, entity, offset, max_id self, entity, offset, max_id
): ):
entity = await self.client.get_input_entity(entity) 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( self.request = functions.photos.GetUserPhotosRequest(
entity, entity,
offset=offset, offset=offset,
@ -864,7 +865,8 @@ class ChatMethods:
""" """
entity = await self.get_input_entity(entity) entity = await self.get_input_entity(entity)
user = await self.get_input_entity(user) 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') raise ValueError('You must pass a user entity')
perm_names = ( perm_names = (
@ -872,7 +874,8 @@ class ChatMethods:
'ban_users', 'invite_users', 'pin_messages', 'add_admins' '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 # If we try to set these permissions in a megagroup, we
# would get a RIGHT_FORBIDDEN. However, it makes sense # would get a RIGHT_FORBIDDEN. However, it makes sense
# that an admin can post messages, so we want to avoid the error # that an admin can post messages, so we want to avoid the error
@ -894,7 +897,7 @@ class ChatMethods:
for name in perm_names for name in perm_names
}), rank=title or '')) }), rank=title or ''))
elif isinstance(entity, types.InputPeerChat): elif ty == helpers._EntityType.CHAT:
# If the user passed any permission in a small # If the user passed any permission in a small
# group chat, they must be a full admin to have it. # group chat, they must be a full admin to have it.
if is_admin is None: if is_admin is None:
@ -1015,7 +1018,8 @@ class ChatMethods:
await client.edit_permissions(chat, user) await client.edit_permissions(chat, user)
""" """
entity = await self.get_input_entity(entity) 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') raise ValueError('You must pass either a channel or a supergroup')
rights = types.ChatBannedRights( rights = types.ChatBannedRights(
@ -1040,12 +1044,13 @@ class ChatMethods:
)) ))
user = await self.get_input_entity(user) 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): if isinstance(user, types.InputPeerSelf):
raise ValueError('You cannot restrict yourself') 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( return await self(functions.channels.EditBannedRequest(
channel=entity, channel=entity,
user_id=user, user_id=user,
@ -1086,12 +1091,13 @@ class ChatMethods:
""" """
entity = await self.get_input_entity(entity) entity = await self.get_input_entity(entity)
user = await self.get_input_entity(user) 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') 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)) await self(functions.messages.DeleteChatUserRequest(entity.chat_id, user))
elif isinstance(entity, types.InputPeerChannel): elif ty == helpers._EntityType.CHANNEL:
if isinstance(user, types.InputPeerSelf): if isinstance(user, types.InputPeerSelf):
await self(functions.channels.LeaveChannelRequest(entity)) await self(functions.channels.LeaveChannelRequest(entity))
else: else:

View File

@ -3,7 +3,7 @@ import inspect
import itertools import itertools
import typing import typing
from .. import utils, hints from .. import helpers, utils, hints
from ..requestiter import RequestIter from ..requestiter import RequestIter
from ..tl import types, functions, custom from ..tl import types, functions, custom
@ -436,10 +436,11 @@ class DialogMethods:
await client.delete_dialog('username') await client.delete_dialog('username')
""" """
entity = await self.get_input_entity(entity) 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)) return await self(functions.channels.LeaveChannelRequest(entity))
if isinstance(entity, types.InputPeerChat): if ty == helpers._EntityType.CHAT:
result = await self(functions.messages.DeleteChatUserRequest( result = await self(functions.messages.DeleteChatUserRequest(
entity.chat_id, types.InputUserSelf())) entity.chat_id, types.InputUserSelf()))
else: else:

View File

@ -257,7 +257,8 @@ class DownloadMethods:
# See issue #500, Android app fails as of v4.6.0 (1155). # See issue #500, Android app fails as of v4.6.0 (1155).
# The fix seems to be using the full channel chat photo. # The fix seems to be using the full channel chat photo.
ie = await self.get_input_entity(entity) 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)) full = await self(functions.channels.GetFullChannelRequest(ie))
return await self._download_photo( return await self._download_photo(
full.full_chat.chat_photo, file, full.full_chat.chat_photo, file,

View File

@ -2,7 +2,7 @@ import itertools
import re import re
import typing import typing
from .. import utils from .. import helpers, utils
from ..tl import types from ..tl import types
if typing.TYPE_CHECKING: if typing.TYPE_CHECKING:
@ -134,7 +134,7 @@ class MessageParseMethods:
id_to_message[update.message.id] = update.message id_to_message[update.message.id] = update.message
elif (isinstance(update, types.UpdateEditMessage) 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: if request.id == update.message.id:
update.message._finish_init(self, entities, input_chat) update.message._finish_init(self, entities, input_chat)
return update.message return update.message

View File

@ -2,7 +2,7 @@ import inspect
import itertools import itertools
import typing import typing
from .. import utils, errors, hints from .. import helpers, utils, errors, hints
from ..requestiter import RequestIter from ..requestiter import RequestIter
from ..tl import types, functions from ..tl import types, functions
@ -57,8 +57,8 @@ class _MessagesIter(RequestIter):
if from_user: if from_user:
from_user = await self.client.get_input_entity(from_user) from_user = await self.client.get_input_entity(from_user)
if not isinstance(from_user, ( ty = helpers._entity_type(from_user)
types.InputPeerUser, types.InputPeerSelf)): if ty != helpers._EntityType.USER:
from_user = None # Ignore from_user unless it's a user from_user = None # Ignore from_user unless it's a user
if from_user: if from_user:
@ -86,8 +86,8 @@ class _MessagesIter(RequestIter):
filter = types.InputMessagesFilterEmpty() filter = types.InputMessagesFilterEmpty()
# Telegram completely ignores `from_id` in private chats # Telegram completely ignores `from_id` in private chats
if isinstance( ty = helpers._entity_type(self.entity)
self.entity, (types.InputPeerUser, types.InputPeerSelf)): if ty == helpers._EntityType.USER:
# Don't bother sending `from_user` (it's ignored anyway), # Don't bother sending `from_user` (it's ignored anyway),
# but keep `from_id` defined above to check it locally. # but keep `from_id` defined above to check it locally.
from_user = None from_user = None
@ -246,6 +246,7 @@ class _IDsIter(RequestIter):
self._ids = list(reversed(ids)) if self.reverse else ids self._ids = list(reversed(ids)) if self.reverse else ids
self._offset = 0 self._offset = 0
self._entity = (await self.client.get_input_entity(entity)) if entity else None 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.) # 30s flood wait every 300 messages (3 requests of 100 each, 30 of 10, etc.)
if self.wait_time is None: if self.wait_time is None:
@ -259,7 +260,7 @@ class _IDsIter(RequestIter):
self._offset += _MAX_CHUNK_SIZE self._offset += _MAX_CHUNK_SIZE
from_id = None # By default, no need to validate from_id 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: try:
r = await self.client( r = await self.client(
functions.channels.GetMessagesRequest(self._entity, ids)) functions.channels.GetMessagesRequest(self._entity, ids))
@ -1108,7 +1109,7 @@ class MessageMethods:
) )
entity = await self.get_input_entity(entity) if entity else None 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( return await self([functions.channels.DeleteMessagesRequest(
entity, list(c)) for c in utils.chunks(message_ids)]) entity, list(c)) for c in utils.chunks(message_ids)])
else: else:
@ -1181,7 +1182,7 @@ class MessageMethods:
return True return True
if max_id is not None: 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( return await self(functions.channels.ReadHistoryRequest(
utils.get_input_channel(entity), max_id=max_id)) utils.get_input_channel(entity), max_id=max_id))
else: else:

View File

@ -4,7 +4,7 @@ import itertools
import time import time
import typing import typing
from .. import errors, utils, hints from .. import errors, helpers, utils, hints
from ..errors import MultiError, RPCError from ..errors import MultiError, RPCError
from ..helpers import retry_range from ..helpers import retry_range
from ..tl import TLRequest, types, functions from ..tl import TLRequest, types, functions
@ -258,12 +258,20 @@ class UserMethods:
else: else:
inputs.append(await self.get_input_entity(x)) inputs.append(await self.get_input_entity(x))
users = [x for x in inputs lists = {
if isinstance(x, (types.InputPeerUser, types.InputPeerSelf))] helpers._EntityType.USER: [],
chats = [x.chat_id for x in inputs helpers._EntityType.CHAT: [],
if isinstance(x, types.InputPeerChat)] helpers._EntityType.CHANNEL: [],
channels = [x for x in inputs }
if isinstance(x, types.InputPeerChannel)] 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: if users:
# GetUsersRequest has a limit of 200 per call # GetUsersRequest has a limit of 200 per call
tmp = [] tmp = []

View File

@ -1,10 +1,17 @@
"""Various helpers not related to the Telegram API itself""" """Various helpers not related to the Telegram API itself"""
import asyncio import asyncio
import enum
import os import os
import struct import struct
from hashlib import sha1 from hashlib import sha1
class _EntityType(enum.Enum):
USER = 0
CHAT = 1
CHANNEL = 2
# region Multiple utilities # region Multiple utilities
@ -131,6 +138,41 @@ def _sync_exit(self, *args):
return loop.run_until_complete(self.__aexit__(*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 # endregion
# region Cryptographic related utils # region Cryptographic related utils

View File

@ -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_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_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 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 MSG_WAIT_FAILED,400,A waiting call returned an error
MT_SEND_QUEUE_TOO_LONG,500, MT_SEND_QUEUE_TOO_LONG,500,
NEED_CHAT_INVALID,500,The provided chat is invalid NEED_CHAT_INVALID,500,The provided chat is invalid

1 name codes description
149 MESSAGE_ID_INVALID 400 The specified message ID is invalid or you can't do that operation on such message
150 MESSAGE_NOT_MODIFIED 400 Content of the message was not modified
151 MESSAGE_TOO_LONG 400 Message was too long. Current maximum length is 4096 UTF-8 characters
152 MSG_ID_INVALID 400 The message ID used in the peer was invalid
153 MSG_WAIT_FAILED 400 A waiting call returned an error
154 MT_SEND_QUEUE_TOO_LONG 500
155 NEED_CHAT_INVALID 500 The provided chat is invalid

View File

@ -238,7 +238,7 @@ messages.sendEncryptedFile,user,MSG_WAIT_FAILED
messages.sendEncryptedService,user,DATA_INVALID ENCRYPTION_DECLINED MSG_WAIT_FAILED USER_IS_BLOCKED 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.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.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.sendMultiMedia,both,SCHEDULE_DATE_TOO_LATE SCHEDULE_TOO_MUCH
messages.sendReaction,User,REACTION_INVALID messages.sendReaction,User,REACTION_INVALID
messages.sendVote,user, messages.sendVote,user,

1 method usability errors
238 messages.sendEncryptedService user DATA_INVALID ENCRYPTION_DECLINED MSG_WAIT_FAILED USER_IS_BLOCKED
239 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
240 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
241 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 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
242 messages.sendMultiMedia both SCHEDULE_DATE_TOO_LATE SCHEDULE_TOO_MUCH
243 messages.sendReaction User REACTION_INVALID
244 messages.sendVote user