mirror of
https://github.com/LonamiWebs/Telethon.git
synced 2025-08-03 11:40:11 +03:00
Merge branch 'master' into asyncio
This commit is contained in:
commit
04a68f12cc
|
@ -204,9 +204,7 @@ class ChatAction(EventBuilder):
|
|||
|
||||
if isinstance(self._pinned_message, int) and await self.input_chat:
|
||||
r = await self._client(functions.channels.GetMessagesRequest(
|
||||
self._input_chat, [
|
||||
types.InputMessageID(self._pinned_message)
|
||||
]
|
||||
self._input_chat, [self._pinned_message]
|
||||
))
|
||||
try:
|
||||
self._pinned_message = next(
|
||||
|
|
|
@ -118,15 +118,11 @@ class EventCommon(abc.ABC):
|
|||
try:
|
||||
if isinstance(chat, types.InputPeerChannel):
|
||||
result = await self._client(
|
||||
functions.channels.GetMessagesRequest(chat, [
|
||||
types.InputMessageID(msg_id)
|
||||
])
|
||||
functions.channels.GetMessagesRequest(chat, [msg_id])
|
||||
)
|
||||
else:
|
||||
result = await self._client(
|
||||
functions.messages.GetMessagesRequest([
|
||||
types.InputMessageID(msg_id)
|
||||
])
|
||||
functions.messages.GetMessagesRequest([msg_id])
|
||||
)
|
||||
except RPCError:
|
||||
return None, None
|
||||
|
|
|
@ -101,16 +101,14 @@ class MessageRead(EventBuilder):
|
|||
if not chat:
|
||||
self._messages = []
|
||||
elif isinstance(chat, types.InputPeerChannel):
|
||||
ids = [types.InputMessageID(x) for x in self._message_ids]
|
||||
self._messages =\
|
||||
await self._client(functions.channels.GetMessagesRequest(
|
||||
chat, ids
|
||||
chat, self._message_ids
|
||||
)).messages
|
||||
else:
|
||||
ids = [types.InputMessageID(x) for x in self._message_ids]
|
||||
self._messages = \
|
||||
await self._client(functions.messages.GetMessagesRequest(
|
||||
ids
|
||||
self._message_ids
|
||||
)).messages
|
||||
|
||||
return self._messages
|
||||
|
|
|
@ -297,13 +297,11 @@ class NewMessage(EventBuilder):
|
|||
if self._reply_message is None:
|
||||
if isinstance(await self.input_chat, types.InputPeerChannel):
|
||||
r = await self._client(functions.channels.GetMessagesRequest(
|
||||
await self.input_chat, [
|
||||
types.InputMessageID(self.message.reply_to_msg_id)
|
||||
]
|
||||
await self.input_chat, [self.message.reply_to_msg_id]
|
||||
))
|
||||
else:
|
||||
r = await self._client(functions.messages.GetMessagesRequest(
|
||||
[types.InputMessageID(self.message.reply_to_msg_id)]
|
||||
[self.message.reply_to_msg_id]
|
||||
))
|
||||
if not isinstance(r, types.messages.MessagesNotModified):
|
||||
self._reply_message = r.messages[0]
|
||||
|
|
|
@ -17,7 +17,7 @@ CURRENT_VERSION = 3 # database version
|
|||
|
||||
class SQLiteSession(MemorySession):
|
||||
"""This session contains the required information to login into your
|
||||
Telegram account. NEVER give the saved JSON file to anyone, since
|
||||
Telegram account. NEVER give the saved session file to anyone, since
|
||||
they would gain instant access to all your messages and contacts.
|
||||
|
||||
If you think the session has been compromised, close all the sessions
|
||||
|
|
|
@ -9,7 +9,8 @@ from .crypto import rsa
|
|||
from .errors import (
|
||||
RPCError, BrokenAuthKeyError, ServerError, FloodWaitError,
|
||||
FloodTestPhoneWaitError, TypeNotFoundError, UnauthorizedError,
|
||||
PhoneMigrateError, NetworkMigrateError, UserMigrateError, AuthKeyError
|
||||
PhoneMigrateError, NetworkMigrateError, UserMigrateError, AuthKeyError,
|
||||
RpcCallFailError
|
||||
)
|
||||
from .network import authenticator, MtProtoSender, Connection, ConnectionMode
|
||||
from .sessions import Session, SQLiteSession
|
||||
|
@ -544,9 +545,9 @@ class TelegramBareClient:
|
|||
await self._reconnect(new_dc=e.new_dc)
|
||||
return await self._invoke(call_receive, retry, *requests)
|
||||
|
||||
except ServerError as e:
|
||||
except (ServerError, RpcCallFailError) as e:
|
||||
# Telegram is having some issues, just retry
|
||||
__log__.error('Telegram servers are having internal errors %s', e)
|
||||
__log__.warning('Telegram is having internal issues: %s', e)
|
||||
|
||||
except (FloodWaitError, FloodTestPhoneWaitError) as e:
|
||||
__log__.warning('Request invoked too often, wait %ds', e.seconds)
|
||||
|
|
|
@ -58,7 +58,7 @@ from .tl.functions.messages import (
|
|||
SendMessageRequest, GetChatsRequest, GetAllDraftsRequest,
|
||||
CheckChatInviteRequest, ReadMentionsRequest, SendMultiMediaRequest,
|
||||
UploadMediaRequest, EditMessageRequest, GetFullChatRequest,
|
||||
ForwardMessagesRequest
|
||||
ForwardMessagesRequest, SearchRequest
|
||||
)
|
||||
|
||||
from .tl.functions import channels
|
||||
|
@ -780,9 +780,13 @@ class TelegramClient(TelegramBareClient):
|
|||
if isinstance(message, Message):
|
||||
if (message.media
|
||||
and not isinstance(message.media, MessageMediaWebPage)):
|
||||
return await self.send_file(entity, message.media)
|
||||
return await self.send_file(entity, message.media,
|
||||
caption=message.message,
|
||||
entities=message.entities)
|
||||
|
||||
if utils.get_peer_id(entity) == utils.get_peer_id(message.to_id):
|
||||
if reply_to is not None:
|
||||
reply_id = self._get_message_id(reply_to)
|
||||
elif utils.get_peer_id(entity) == utils.get_peer_id(message.to_id):
|
||||
reply_id = message.reply_to_msg_id
|
||||
else:
|
||||
reply_id = None
|
||||
|
@ -879,20 +883,26 @@ class TelegramClient(TelegramBareClient):
|
|||
result = [id_to_message[random_to_id[rnd]] for rnd in req.random_id]
|
||||
return result[0] if single else result
|
||||
|
||||
async def edit_message(self, entity, message_id, message=None,
|
||||
async def edit_message(self, entity, message=None, text=None,
|
||||
parse_mode='md', link_preview=True):
|
||||
"""
|
||||
Edits the given message ID (to change its contents or disable preview).
|
||||
|
||||
Args:
|
||||
entity (`entity`):
|
||||
From which chat to edit the message.
|
||||
entity (`entity` | :tl:`Message`):
|
||||
From which chat to edit the message. This can also be
|
||||
the message to be edited, and the entity will be inferred
|
||||
from it, so the next parameter will be assumed to be the
|
||||
message text.
|
||||
|
||||
message_id (`str`):
|
||||
The ID of the message (or ``Message`` itself) to be edited.
|
||||
message (`int` | :tl:`Message` | `str`):
|
||||
The ID of the message (or :tl:`Message` itself) to be edited.
|
||||
If the `entity` was a :tl:`Message`, then this message will be
|
||||
treated as the new text.
|
||||
|
||||
message (`str`, optional):
|
||||
The new text of the message.
|
||||
text (`str`, optional):
|
||||
The new text of the message. Does nothing if the `entity`
|
||||
was a :tl:`Message`.
|
||||
|
||||
parse_mode (`str`, optional):
|
||||
Can be 'md' or 'markdown' for markdown-like parsing (default),
|
||||
|
@ -903,6 +913,21 @@ class TelegramClient(TelegramBareClient):
|
|||
link_preview (`bool`, optional):
|
||||
Should the link preview be shown?
|
||||
|
||||
Examples:
|
||||
|
||||
>>> async def main():
|
||||
... client = await TelegramClient(...).start()
|
||||
... message = await client.send_message('username', 'hello')
|
||||
...
|
||||
... await client.edit_message('username', message, 'hello!')
|
||||
... # or
|
||||
... await client.edit_message('username', message.id, 'Hello')
|
||||
... # or
|
||||
... await client.edit_message(message, 'Hello!')
|
||||
...
|
||||
>>> loop = ...
|
||||
>>> loop.run_until_complete(main())
|
||||
|
||||
Raises:
|
||||
``MessageAuthorRequiredError`` if you're not the author of the
|
||||
message but try editing it anyway.
|
||||
|
@ -913,12 +938,16 @@ class TelegramClient(TelegramBareClient):
|
|||
Returns:
|
||||
The edited :tl:`Message`.
|
||||
"""
|
||||
message, msg_entities =\
|
||||
await self._parse_message_text(message, parse_mode)
|
||||
if isinstance(entity, Message):
|
||||
text = message # Shift the parameters to the right
|
||||
message = entity
|
||||
entity = entity.to_id
|
||||
|
||||
text, msg_entities = await self._parse_message_text(text, parse_mode)
|
||||
request = EditMessageRequest(
|
||||
peer=await self.get_input_entity(entity),
|
||||
id=self._get_message_id(message_id),
|
||||
message=message,
|
||||
id=self._get_message_id(message),
|
||||
message=text,
|
||||
no_webpage=not link_preview,
|
||||
entities=msg_entities
|
||||
)
|
||||
|
@ -967,10 +996,14 @@ class TelegramClient(TelegramBareClient):
|
|||
|
||||
async def iter_messages(self, entity, limit=20, offset_date=None,
|
||||
offset_id=0, max_id=0, min_id=0, add_offset=0,
|
||||
search=None, filter=None, from_user=None,
|
||||
batch_size=100, wait_time=None, _total=None):
|
||||
"""
|
||||
Iterator over the message history for the specified entity.
|
||||
|
||||
If either `search`, `filter` or `from_user` are provided,
|
||||
:tl:`messages.Search` will be used instead of :tl:`messages.getHistory`.
|
||||
|
||||
Args:
|
||||
entity (`entity`):
|
||||
The entity from whom to retrieve the message history.
|
||||
|
@ -1002,6 +1035,17 @@ class TelegramClient(TelegramBareClient):
|
|||
Additional message offset (all of the specified offsets +
|
||||
this offset = older messages).
|
||||
|
||||
search (`str`):
|
||||
The string to be used as a search query.
|
||||
|
||||
filter (:tl:`MessagesFilter` | `type`):
|
||||
The filter to use when returning messages. For instance,
|
||||
:tl:`InputMessagesFilterPhotos` would yield only messages
|
||||
containing photos.
|
||||
|
||||
from_user (`entity`):
|
||||
Only messages from this user will be returned.
|
||||
|
||||
batch_size (`int`):
|
||||
Messages will be returned in chunks of this size (100 is
|
||||
the maximum). While it makes no sense to modify this value,
|
||||
|
@ -1033,15 +1077,37 @@ class TelegramClient(TelegramBareClient):
|
|||
"""
|
||||
entity = await self.get_input_entity(entity)
|
||||
limit = float('inf') if limit is None else int(limit)
|
||||
if search is not None or filter or from_user:
|
||||
request = SearchRequest(
|
||||
peer=entity,
|
||||
q=search or '',
|
||||
filter=filter() if isinstance(filter, type) else filter,
|
||||
min_date=None,
|
||||
max_date=offset_date,
|
||||
offset_id=offset_id,
|
||||
add_offset=add_offset,
|
||||
limit=1,
|
||||
max_id=max_id,
|
||||
min_id=min_id,
|
||||
from_id=self.get_input_entity(from_user) if from_user else None
|
||||
)
|
||||
else:
|
||||
request = GetHistoryRequest(
|
||||
peer=entity,
|
||||
limit=1,
|
||||
offset_date=offset_date,
|
||||
offset_id=offset_id,
|
||||
min_id=min_id,
|
||||
max_id=max_id,
|
||||
add_offset=add_offset,
|
||||
hash=0
|
||||
)
|
||||
|
||||
if limit == 0:
|
||||
if not _total:
|
||||
return
|
||||
# No messages, but we still need to know the total message count
|
||||
result = await self(GetHistoryRequest(
|
||||
peer=entity, limit=1,
|
||||
offset_date=None, offset_id=0, max_id=0, min_id=0,
|
||||
add_offset=0, hash=0
|
||||
))
|
||||
result = await self(request)
|
||||
_total[0] = getattr(result, 'count', len(result.messages))
|
||||
return
|
||||
|
||||
|
@ -1052,17 +1118,8 @@ class TelegramClient(TelegramBareClient):
|
|||
batch_size = min(max(batch_size, 1), 100)
|
||||
while have < limit:
|
||||
# Telegram has a hard limit of 100
|
||||
real_limit = min(limit - have, batch_size)
|
||||
r = await self(GetHistoryRequest(
|
||||
peer=entity,
|
||||
limit=real_limit,
|
||||
offset_date=offset_date,
|
||||
offset_id=offset_id,
|
||||
max_id=max_id,
|
||||
min_id=min_id,
|
||||
add_offset=add_offset,
|
||||
hash=0
|
||||
))
|
||||
request.limit = min(limit - have, batch_size)
|
||||
r = await self(request)
|
||||
if _total:
|
||||
_total[0] = getattr(r, 'count', len(r.messages))
|
||||
|
||||
|
@ -1097,11 +1154,15 @@ class TelegramClient(TelegramBareClient):
|
|||
yield message
|
||||
have += 1
|
||||
|
||||
if len(r.messages) < real_limit:
|
||||
if len(r.messages) < request.limit:
|
||||
break
|
||||
|
||||
offset_id = r.messages[-1].id
|
||||
offset_date = r.messages[-1].date
|
||||
request.offset_id = r.messages[-1].id
|
||||
if isinstance(request, GetHistoryRequest):
|
||||
request.offset_date = r.messages[-1].date
|
||||
else:
|
||||
request.max_date = r.messages[-1].date
|
||||
|
||||
await asyncio.sleep(wait_time)
|
||||
|
||||
async def get_messages(self, *args, **kwargs):
|
||||
|
@ -1479,8 +1540,14 @@ class TelegramClient(TelegramBareClient):
|
|||
|
||||
entity = await self.get_input_entity(entity)
|
||||
reply_to = self._get_message_id(reply_to)
|
||||
caption, msg_entities =\
|
||||
await self._parse_message_text(caption, parse_mode)
|
||||
|
||||
# Not document since it's subject to change.
|
||||
# Needed when a Message is passed to send_message and it has media.
|
||||
if 'entities' in kwargs:
|
||||
msg_entities = kwargs['entities']
|
||||
else:
|
||||
caption, msg_entities =\
|
||||
await self._parse_message_text(caption, parse_mode)
|
||||
|
||||
if not isinstance(file, (str, bytes, io.IOBase)):
|
||||
# The user may pass a Message containing media (or the media,
|
||||
|
@ -2481,7 +2548,11 @@ class TelegramClient(TelegramBareClient):
|
|||
If in the end the access hash required for the peer was not found,
|
||||
a ValueError will be raised.
|
||||
Returns:
|
||||
:tl:`InputPeerUser`, :tl:`InputPeerChat` or :tl:`InputPeerChannel`.
|
||||
:tl:`InputPeerUser`, :tl:`InputPeerChat` or :tl:`InputPeerChannel`
|
||||
or :tl:`InputPeerSelf` if the parameter is ``'me'`` or ``'self'``.
|
||||
|
||||
If you need to get the ID of yourself, you should use
|
||||
`get_me` with ``input_peer=True``) instead.
|
||||
"""
|
||||
if peer in ('me', 'self'):
|
||||
return InputPeerSelf()
|
||||
|
|
|
@ -25,7 +25,7 @@ from .tl.types import (
|
|||
InputPhotoEmpty, FileLocation, ChatPhotoEmpty, UserProfilePhotoEmpty,
|
||||
FileLocationUnavailable, InputMediaUploadedDocument, ChannelFull,
|
||||
InputMediaUploadedPhoto, DocumentAttributeFilename, photos,
|
||||
TopPeer, InputNotifyPeer
|
||||
TopPeer, InputNotifyPeer, InputMessageID
|
||||
)
|
||||
from .tl.types.contacts import ResolvedPeer
|
||||
|
||||
|
@ -255,8 +255,12 @@ def get_input_media(media, is_photo=False):
|
|||
it will be treated as an :tl:`InputMediaUploadedPhoto`.
|
||||
"""
|
||||
try:
|
||||
if media.SUBCLASS_OF_ID == 0xfaf846f4: # crc32(b'InputMedia'):
|
||||
if media.SUBCLASS_OF_ID == 0xfaf846f4: # crc32(b'InputMedia')
|
||||
return media
|
||||
elif media.SUBCLASS_OF_ID == 0x846363e0: # crc32(b'InputPhoto')
|
||||
return InputMediaPhoto(media)
|
||||
elif media.SUBCLASS_OF_ID == 0xf33fdb68: # crc32(b'InputDocument')
|
||||
return InputMediaDocument(media)
|
||||
except AttributeError:
|
||||
_raise_cast_fail(media, 'InputMedia')
|
||||
|
||||
|
@ -333,6 +337,21 @@ def get_input_media(media, is_photo=False):
|
|||
_raise_cast_fail(media, 'InputMedia')
|
||||
|
||||
|
||||
def get_input_message(message):
|
||||
"""Similar to :meth:`get_input_peer`, but for input messages."""
|
||||
try:
|
||||
if isinstance(message, int): # This case is really common too
|
||||
return InputMessageID(message)
|
||||
elif message.SUBCLASS_OF_ID == 0x54b6bcc5: # crc32(b'InputMessage'):
|
||||
return message
|
||||
elif message.SUBCLASS_OF_ID == 0x790009e3: # crc32(b'Message'):
|
||||
return InputMessageID(message.id)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
_raise_cast_fail(message, 'InputMedia')
|
||||
|
||||
|
||||
def is_image(file):
|
||||
"""
|
||||
Returns ``True`` if the file extension looks like an image file to Telegram.
|
||||
|
|
|
@ -18,7 +18,8 @@ AUTO_CASTS = {
|
|||
'InputChannel': 'utils.get_input_channel(await client.get_input_entity({}))',
|
||||
'InputUser': 'utils.get_input_user(await client.get_input_entity({}))',
|
||||
'InputMedia': 'utils.get_input_media({})',
|
||||
'InputPhoto': 'utils.get_input_photo({})'
|
||||
'InputPhoto': 'utils.get_input_photo({})',
|
||||
'InputMessage': 'utils.get_input_message({})'
|
||||
}
|
||||
|
||||
BASE_TYPES = ('string', 'bytes', 'int', 'long', 'int128',
|
||||
|
|
Loading…
Reference in New Issue
Block a user