mirror of
https://github.com/LonamiWebs/Telethon.git
synced 2024-11-22 09:26:37 +03:00
Support sending scheduled messages
This commit is contained in:
parent
9dd73cd494
commit
dab237e758
|
@ -123,6 +123,7 @@ class MessageParseMethods:
|
||||||
|
|
||||||
random_to_id = {}
|
random_to_id = {}
|
||||||
id_to_message = {}
|
id_to_message = {}
|
||||||
|
sched_to_message = {} # scheduled IDs may collide with normal IDs
|
||||||
for update in updates:
|
for update in updates:
|
||||||
if isinstance(update, types.UpdateMessageID):
|
if isinstance(update, types.UpdateMessageID):
|
||||||
random_to_id[update.random_id] = update.id
|
random_to_id[update.random_id] = update.id
|
||||||
|
@ -145,12 +146,30 @@ class MessageParseMethods:
|
||||||
update.message._finish_init(self, entities, input_chat)
|
update.message._finish_init(self, entities, input_chat)
|
||||||
return update.message
|
return update.message
|
||||||
|
|
||||||
|
elif isinstance(update, types.UpdateNewScheduledMessage):
|
||||||
|
update.message._finish_init(self, entities, input_chat)
|
||||||
|
sched_to_message[update.message.id] = update.message
|
||||||
|
|
||||||
if request is None:
|
if request is None:
|
||||||
return id_to_message
|
return id_to_message
|
||||||
|
|
||||||
|
# Use the scheduled mapping if we got a request with a scheduled message
|
||||||
|
#
|
||||||
|
# This breaks if the schedule date is too young, however, since the message
|
||||||
|
# is sent immediately, so have a fallback.
|
||||||
|
if getattr(request, 'schedule_date', None) is None:
|
||||||
|
mapping = id_to_message
|
||||||
|
opposite = {} # if there's no schedule it can never be scheduled
|
||||||
|
else:
|
||||||
|
mapping = sched_to_message
|
||||||
|
opposite = id_to_message # scheduled may be treated as normal, though
|
||||||
|
|
||||||
random_id = request if isinstance(request, int) else request.random_id
|
random_id = request if isinstance(request, int) else request.random_id
|
||||||
if not utils.is_list_like(random_id):
|
if not utils.is_list_like(random_id):
|
||||||
msg = id_to_message.get(random_to_id.get(random_id))
|
msg = mapping.get(random_to_id.get(random_id))
|
||||||
|
if not msg:
|
||||||
|
msg = opposite.get(random_to_id.get(random_id))
|
||||||
|
|
||||||
if not msg:
|
if not msg:
|
||||||
self._log[__name__].warning(
|
self._log[__name__].warning(
|
||||||
'Request %s had missing message mapping %s', request, result)
|
'Request %s had missing message mapping %s', request, result)
|
||||||
|
@ -158,15 +177,22 @@ class MessageParseMethods:
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return [id_to_message[random_to_id[rnd]] for rnd in random_id]
|
return [mapping[random_to_id[rnd]] for rnd in random_id]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
# Sometimes forwards fail (`MESSAGE_ID_INVALID` if a message gets
|
try:
|
||||||
# deleted or `WORKER_BUSY_TOO_LONG_RETRY` if there are issues at
|
return [opposite[random_to_id[rnd]] for rnd in random_id]
|
||||||
# Telegram), in which case we get some "missing" message mappings.
|
except KeyError:
|
||||||
# Log them with the hope that we can better work around them.
|
# Sometimes forwards fail (`MESSAGE_ID_INVALID` if a message gets
|
||||||
self._log[__name__].warning(
|
# deleted or `WORKER_BUSY_TOO_LONG_RETRY` if there are issues at
|
||||||
'Request %s had missing message mappings %s', request, result)
|
# Telegram), in which case we get some "missing" message mappings.
|
||||||
|
# Log them with the hope that we can better work around them.
|
||||||
|
self._log[__name__].warning(
|
||||||
|
'Request %s had missing message mappings %s', request, result)
|
||||||
|
|
||||||
return [id_to_message.get(random_to_id.get(rnd)) for rnd in random_to_id]
|
return [
|
||||||
|
mapping.get(random_to_id.get(rnd))
|
||||||
|
or opposite.get(random_to_id.get(rnd))
|
||||||
|
for rnd in random_to_id
|
||||||
|
]
|
||||||
|
|
||||||
# endregion
|
# endregion
|
||||||
|
|
|
@ -536,7 +536,9 @@ class MessageMethods:
|
||||||
force_document: bool = False,
|
force_document: bool = False,
|
||||||
clear_draft: bool = False,
|
clear_draft: bool = False,
|
||||||
buttons: 'hints.MarkupLike' = None,
|
buttons: 'hints.MarkupLike' = None,
|
||||||
silent: bool = None) -> 'types.Message':
|
silent: bool = None,
|
||||||
|
schedule: 'hints.DateLike' = None
|
||||||
|
) -> 'types.Message':
|
||||||
"""
|
"""
|
||||||
Sends a message to the specified user, chat or channel.
|
Sends a message to the specified user, chat or channel.
|
||||||
|
|
||||||
|
@ -609,6 +611,11 @@ class MessageMethods:
|
||||||
channel or not. Defaults to `False`, which means it will
|
channel or not. Defaults to `False`, which means it will
|
||||||
notify them. Set it to `True` to alter this behaviour.
|
notify them. Set it to `True` to alter this behaviour.
|
||||||
|
|
||||||
|
schedule (`hints.DateLike`, optional):
|
||||||
|
If set, the message won't send immediately, and instead
|
||||||
|
it will be scheduled to be automatically sent at a later
|
||||||
|
time.
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
The sent `custom.Message <telethon.tl.custom.message.Message>`.
|
The sent `custom.Message <telethon.tl.custom.message.Message>`.
|
||||||
|
|
||||||
|
@ -663,6 +670,10 @@ class MessageMethods:
|
||||||
# Forcing replies or clearing buttons.
|
# Forcing replies or clearing buttons.
|
||||||
await client.send_message(chat, 'Reply to me', buttons=Button.force_reply())
|
await client.send_message(chat, 'Reply to me', buttons=Button.force_reply())
|
||||||
await client.send_message(chat, 'Bye Keyboard!', buttons=Button.clear())
|
await client.send_message(chat, 'Bye Keyboard!', buttons=Button.clear())
|
||||||
|
|
||||||
|
# Scheduling a message to be sent after 5 minutes
|
||||||
|
from datetime import timedelta
|
||||||
|
await client.send_message(chat, 'Hi, future!', schedule=timedelta(minutes=5))
|
||||||
"""
|
"""
|
||||||
if file is not None:
|
if file is not None:
|
||||||
return await self.send_file(
|
return await self.send_file(
|
||||||
|
@ -690,7 +701,8 @@ class MessageMethods:
|
||||||
silent=silent,
|
silent=silent,
|
||||||
reply_to=reply_to,
|
reply_to=reply_to,
|
||||||
buttons=markup,
|
buttons=markup,
|
||||||
entities=message.entities
|
entities=message.entities,
|
||||||
|
schedule=schedule
|
||||||
)
|
)
|
||||||
|
|
||||||
request = functions.messages.SendMessageRequest(
|
request = functions.messages.SendMessageRequest(
|
||||||
|
@ -702,7 +714,8 @@ class MessageMethods:
|
||||||
entities=message.entities,
|
entities=message.entities,
|
||||||
clear_draft=clear_draft,
|
clear_draft=clear_draft,
|
||||||
no_webpage=not isinstance(
|
no_webpage=not isinstance(
|
||||||
message.media, types.MessageMediaWebPage)
|
message.media, types.MessageMediaWebPage),
|
||||||
|
schedule_date=schedule
|
||||||
)
|
)
|
||||||
message = message.message
|
message = message.message
|
||||||
else:
|
else:
|
||||||
|
@ -720,7 +733,8 @@ class MessageMethods:
|
||||||
reply_to_msg_id=utils.get_message_id(reply_to),
|
reply_to_msg_id=utils.get_message_id(reply_to),
|
||||||
clear_draft=clear_draft,
|
clear_draft=clear_draft,
|
||||||
silent=silent,
|
silent=silent,
|
||||||
reply_markup=self.build_reply_markup(buttons)
|
reply_markup=self.build_reply_markup(buttons),
|
||||||
|
schedule_date=schedule
|
||||||
)
|
)
|
||||||
|
|
||||||
result = await self(request)
|
result = await self(request)
|
||||||
|
@ -747,7 +761,9 @@ class MessageMethods:
|
||||||
from_peer: 'hints.EntityLike' = None,
|
from_peer: 'hints.EntityLike' = None,
|
||||||
*,
|
*,
|
||||||
silent: bool = None,
|
silent: bool = None,
|
||||||
as_album: bool = None) -> 'typing.Sequence[types.Message]':
|
as_album: bool = None,
|
||||||
|
schedule: 'hints.DateLike' = None
|
||||||
|
) -> 'typing.Sequence[types.Message]':
|
||||||
"""
|
"""
|
||||||
Forwards the given messages to the specified entity.
|
Forwards the given messages to the specified entity.
|
||||||
|
|
||||||
|
@ -787,6 +803,11 @@ class MessageMethods:
|
||||||
`True` will group always (even converting separate
|
`True` will group always (even converting separate
|
||||||
images into albums), and `False` will never group.
|
images into albums), and `False` will never group.
|
||||||
|
|
||||||
|
schedule (`hints.DateLike`, optional):
|
||||||
|
If set, the message(s) won't forward immediately, and
|
||||||
|
instead they will be scheduled to be automatically sent
|
||||||
|
at a later time.
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
The list of forwarded `Message <telethon.tl.custom.message.Message>`,
|
The list of forwarded `Message <telethon.tl.custom.message.Message>`,
|
||||||
or a single one if a list wasn't provided as input.
|
or a single one if a list wasn't provided as input.
|
||||||
|
@ -873,7 +894,8 @@ class MessageMethods:
|
||||||
# Trying to send a single message as grouped will cause
|
# Trying to send a single message as grouped will cause
|
||||||
# GROUPED_MEDIA_INVALID. If more than one message is forwarded
|
# GROUPED_MEDIA_INVALID. If more than one message is forwarded
|
||||||
# (even without media...), this error goes away.
|
# (even without media...), this error goes away.
|
||||||
grouped=len(chunk) > 1 and grouped
|
grouped=len(chunk) > 1 and grouped,
|
||||||
|
schedule_date=schedule
|
||||||
)
|
)
|
||||||
result = await self(req)
|
result = await self(req)
|
||||||
sent.extend(self._get_response_message(req, result, entity))
|
sent.extend(self._get_response_message(req, result, entity))
|
||||||
|
@ -889,7 +911,9 @@ class MessageMethods:
|
||||||
parse_mode: str = (),
|
parse_mode: str = (),
|
||||||
link_preview: bool = True,
|
link_preview: bool = True,
|
||||||
file: 'hints.FileLike' = None,
|
file: 'hints.FileLike' = None,
|
||||||
buttons: 'hints.MarkupLike' = None) -> 'types.Message':
|
buttons: 'hints.MarkupLike' = None,
|
||||||
|
schedule: 'hints.DateLike' = None
|
||||||
|
) -> 'types.Message':
|
||||||
"""
|
"""
|
||||||
Edits the given message to change its text or media.
|
Edits the given message to change its text or media.
|
||||||
|
|
||||||
|
@ -936,6 +960,14 @@ class MessageMethods:
|
||||||
you have signed in as a bot. You can also pass your own
|
you have signed in as a bot. You can also pass your own
|
||||||
:tl:`ReplyMarkup` here.
|
:tl:`ReplyMarkup` here.
|
||||||
|
|
||||||
|
schedule (`hints.DateLike`, optional):
|
||||||
|
If set, the message won't be edited immediately, and instead
|
||||||
|
it will be scheduled to be automatically edited at a later
|
||||||
|
time.
|
||||||
|
|
||||||
|
Note that this parameter will have no effect if you are
|
||||||
|
trying to edit a message that was sent via inline bots.
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
The edited `Message <telethon.tl.custom.message.Message>`,
|
The edited `Message <telethon.tl.custom.message.Message>`,
|
||||||
unless `entity` was a :tl:`InputBotInlineMessageID` in which
|
unless `entity` was a :tl:`InputBotInlineMessageID` in which
|
||||||
|
@ -993,7 +1025,8 @@ class MessageMethods:
|
||||||
no_webpage=not link_preview,
|
no_webpage=not link_preview,
|
||||||
entities=msg_entities,
|
entities=msg_entities,
|
||||||
media=media,
|
media=media,
|
||||||
reply_markup=self.build_reply_markup(buttons)
|
reply_markup=self.build_reply_markup(buttons),
|
||||||
|
schedule_date=schedule
|
||||||
)
|
)
|
||||||
msg = self._get_response_message(request, await self(request), entity)
|
msg = self._get_response_message(request, await self(request), entity)
|
||||||
await self._cache_media(msg, file, file_handle, image=image)
|
await self._cache_media(msg, file, file_handle, image=image)
|
||||||
|
|
|
@ -104,6 +104,7 @@ class UploadMethods:
|
||||||
buttons: 'hints.MarkupLike' = None,
|
buttons: 'hints.MarkupLike' = None,
|
||||||
silent: bool = None,
|
silent: bool = None,
|
||||||
supports_streaming: bool = False,
|
supports_streaming: bool = False,
|
||||||
|
schedule: 'hints.DateLike' = None,
|
||||||
**kwargs) -> 'types.Message':
|
**kwargs) -> 'types.Message':
|
||||||
"""
|
"""
|
||||||
Sends message with the given file to the specified entity.
|
Sends message with the given file to the specified entity.
|
||||||
|
@ -230,6 +231,11 @@ class UploadMethods:
|
||||||
these to MP4 before sending if you want them to be streamable.
|
these to MP4 before sending if you want them to be streamable.
|
||||||
Unsupported formats will result in ``VideoContentTypeError``.
|
Unsupported formats will result in ``VideoContentTypeError``.
|
||||||
|
|
||||||
|
schedule (`hints.DateLike`, optional):
|
||||||
|
If set, the file won't send immediately, and instead
|
||||||
|
it will be scheduled to be automatically sent at a later
|
||||||
|
time.
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
The `Message <telethon.tl.custom.message.Message>` (or messages)
|
The `Message <telethon.tl.custom.message.Message>` (or messages)
|
||||||
containing the sent file, or messages if a list of them was passed.
|
containing the sent file, or messages if a list of them was passed.
|
||||||
|
@ -295,7 +301,7 @@ class UploadMethods:
|
||||||
result += await self._send_album(
|
result += await self._send_album(
|
||||||
entity, images[:10], caption=image_captions[:10],
|
entity, images[:10], caption=image_captions[:10],
|
||||||
progress_callback=progress_callback, reply_to=reply_to,
|
progress_callback=progress_callback, reply_to=reply_to,
|
||||||
parse_mode=parse_mode, silent=silent
|
parse_mode=parse_mode, silent=silent, schedule=schedule
|
||||||
)
|
)
|
||||||
images = images[10:]
|
images = images[10:]
|
||||||
image_captions = image_captions[10:]
|
image_captions = image_captions[10:]
|
||||||
|
@ -307,7 +313,7 @@ class UploadMethods:
|
||||||
progress_callback=progress_callback, reply_to=reply_to,
|
progress_callback=progress_callback, reply_to=reply_to,
|
||||||
attributes=attributes, thumb=thumb, voice_note=voice_note,
|
attributes=attributes, thumb=thumb, voice_note=voice_note,
|
||||||
video_note=video_note, buttons=buttons, silent=silent,
|
video_note=video_note, buttons=buttons, silent=silent,
|
||||||
supports_streaming=supports_streaming,
|
supports_streaming=supports_streaming, schedule=schedule,
|
||||||
**kwargs
|
**kwargs
|
||||||
))
|
))
|
||||||
|
|
||||||
|
@ -339,7 +345,8 @@ class UploadMethods:
|
||||||
markup = self.build_reply_markup(buttons)
|
markup = self.build_reply_markup(buttons)
|
||||||
request = functions.messages.SendMediaRequest(
|
request = functions.messages.SendMediaRequest(
|
||||||
entity, media, reply_to_msg_id=reply_to, message=caption,
|
entity, media, reply_to_msg_id=reply_to, message=caption,
|
||||||
entities=msg_entities, reply_markup=markup, silent=silent
|
entities=msg_entities, reply_markup=markup, silent=silent,
|
||||||
|
schedule_date=schedule
|
||||||
)
|
)
|
||||||
msg = self._get_response_message(request, await self(request), entity)
|
msg = self._get_response_message(request, await self(request), entity)
|
||||||
await self._cache_media(msg, file, file_handle, image=image)
|
await self._cache_media(msg, file, file_handle, image=image)
|
||||||
|
@ -348,7 +355,7 @@ class UploadMethods:
|
||||||
|
|
||||||
async def _send_album(self: 'TelegramClient', entity, files, caption='',
|
async def _send_album(self: 'TelegramClient', entity, files, caption='',
|
||||||
progress_callback=None, reply_to=None,
|
progress_callback=None, reply_to=None,
|
||||||
parse_mode=(), silent=None):
|
parse_mode=(), silent=None, schedule=None):
|
||||||
"""Specialized version of .send_file for albums"""
|
"""Specialized version of .send_file for albums"""
|
||||||
# We don't care if the user wants to avoid cache, we will use it
|
# We don't care if the user wants to avoid cache, we will use it
|
||||||
# anyway. Why? The cached version will be exactly the same thing
|
# anyway. Why? The cached version will be exactly the same thing
|
||||||
|
@ -398,7 +405,8 @@ class UploadMethods:
|
||||||
|
|
||||||
# Now we can construct the multi-media request
|
# Now we can construct the multi-media request
|
||||||
result = await self(functions.messages.SendMultiMediaRequest(
|
result = await self(functions.messages.SendMultiMediaRequest(
|
||||||
entity, reply_to_msg_id=reply_to, multi_media=media, silent=silent
|
entity, reply_to_msg_id=reply_to, multi_media=media,
|
||||||
|
silent=silent, schedule_date=schedule
|
||||||
))
|
))
|
||||||
|
|
||||||
# We never sent a `random_id` for the messages that resulted from
|
# We never sent a `random_id` for the messages that resulted from
|
||||||
|
|
Loading…
Reference in New Issue
Block a user