mirror of
https://github.com/LonamiWebs/Telethon.git
synced 2025-02-03 05:04:33 +03:00
Replace weird mixin Client classes with free-standing defs
This should take care of the extremely precarious subclassing order. It should also make IDEs go a lot less crazy. Documentation and code can be kept separated.
This commit is contained in:
parent
af201c12ba
commit
f639992baa
|
@ -13,12 +13,27 @@ from Telethon version 1.x to 2.0 onwards.
|
|||
User, chat and channel identifiers are now 64-bit numbers
|
||||
---------------------------------------------------------
|
||||
|
||||
`Layer 133 <https://diff.telethon.dev/?from=132&to=133>`__ changed *a lot* of
|
||||
identifiers from ``int`` to ``long``, meaning they will no longer fit in 32
|
||||
bits, and instead require 64 bits.
|
||||
`Layer 133 <https://diff.telethon.dev/?from=132&to=133>`__ changed *a lot* of identifiers from
|
||||
``int`` to ``long``, meaning they will no longer fit in 32 bits, and instead require 64 bits.
|
||||
|
||||
If you were storing these identifiers somewhere size did matter (for example,
|
||||
a database), you will need to migrate that to support the new size requirement
|
||||
of 8 bytes.
|
||||
If you were storing these identifiers somewhere size did matter (for example, a database), you
|
||||
will need to migrate that to support the new size requirement of 8 bytes.
|
||||
|
||||
For the full list of types changed, please review the above link.
|
||||
|
||||
|
||||
Many modules are now private
|
||||
----------------------------
|
||||
|
||||
There were a lot of things which were public but should not have been. From now on, you should
|
||||
only rely on things that are either publicly re-exported or defined. That is, as soon as anything
|
||||
starts with an underscore (``_``) on its name, you're acknowledging that the functionality may
|
||||
change even across minor version changes, and thus have your code break.
|
||||
|
||||
* The ``telethon.client`` module is now ``telethon._client``, meaning you should stop relying on
|
||||
anything inside of it. This includes all of the subclasses that used to exist (like ``UserMethods``).
|
||||
|
||||
TODO REVIEW self\._\w+\(
|
||||
and __signature__
|
||||
and property
|
||||
abs abc abstract
|
|
@ -1,4 +1,4 @@
|
|||
from .client.telegramclient import TelegramClient
|
||||
from ._client.telegramclient import TelegramClient
|
||||
from .network import connection
|
||||
from .tl import types, functions, custom
|
||||
from .tl.custom import Button
|
||||
|
|
|
@ -107,7 +107,6 @@ class _TakeoutClient:
|
|||
return setattr(self.__client, name, value)
|
||||
|
||||
|
||||
class AccountMethods:
|
||||
def takeout(
|
||||
self: 'TelegramClient',
|
||||
finalize: bool = True,
|
||||
|
@ -119,87 +118,6 @@ class AccountMethods:
|
|||
channels: bool = None,
|
||||
files: bool = None,
|
||||
max_file_size: bool = None) -> 'TelegramClient':
|
||||
"""
|
||||
Returns a :ref:`telethon-client` which calls methods behind a takeout session.
|
||||
|
||||
It does so by creating a proxy object over the current client through
|
||||
which making requests will use :tl:`InvokeWithTakeoutRequest` to wrap
|
||||
them. In other words, returns the current client modified so that
|
||||
requests are done as a takeout:
|
||||
|
||||
Some of the calls made through the takeout session will have lower
|
||||
flood limits. This is useful if you want to export the data from
|
||||
conversations or mass-download media, since the rate limits will
|
||||
be lower. Only some requests will be affected, and you will need
|
||||
to adjust the `wait_time` of methods like `client.iter_messages
|
||||
<telethon.client.messages.MessageMethods.iter_messages>`.
|
||||
|
||||
By default, all parameters are `None`, and you need to enable those
|
||||
you plan to use by setting them to either `True` or `False`.
|
||||
|
||||
You should ``except errors.TakeoutInitDelayError as e``, since this
|
||||
exception will raise depending on the condition of the session. You
|
||||
can then access ``e.seconds`` to know how long you should wait for
|
||||
before calling the method again.
|
||||
|
||||
There's also a `success` property available in the takeout proxy
|
||||
object, so from the `with` body you can set the boolean result that
|
||||
will be sent back to Telegram. But if it's left `None` as by
|
||||
default, then the action is based on the `finalize` parameter. If
|
||||
it's `True` then the takeout will be finished, and if no exception
|
||||
occurred during it, then `True` will be considered as a result.
|
||||
Otherwise, the takeout will not be finished and its ID will be
|
||||
preserved for future usage as `client.session.takeout_id
|
||||
<telethon.sessions.abstract.Session.takeout_id>`.
|
||||
|
||||
Arguments
|
||||
finalize (`bool`):
|
||||
Whether the takeout session should be finalized upon
|
||||
exit or not.
|
||||
|
||||
contacts (`bool`):
|
||||
Set to `True` if you plan on downloading contacts.
|
||||
|
||||
users (`bool`):
|
||||
Set to `True` if you plan on downloading information
|
||||
from users and their private conversations with you.
|
||||
|
||||
chats (`bool`):
|
||||
Set to `True` if you plan on downloading information
|
||||
from small group chats, such as messages and media.
|
||||
|
||||
megagroups (`bool`):
|
||||
Set to `True` if you plan on downloading information
|
||||
from megagroups (channels), such as messages and media.
|
||||
|
||||
channels (`bool`):
|
||||
Set to `True` if you plan on downloading information
|
||||
from broadcast channels, such as messages and media.
|
||||
|
||||
files (`bool`):
|
||||
Set to `True` if you plan on downloading media and
|
||||
you don't only wish to export messages.
|
||||
|
||||
max_file_size (`int`):
|
||||
The maximum file size, in bytes, that you plan
|
||||
to download for each message with media.
|
||||
|
||||
Example
|
||||
.. code-block:: python
|
||||
|
||||
from telethon import errors
|
||||
|
||||
try:
|
||||
async with client.takeout() as takeout:
|
||||
await client.get_messages('me') # normal call
|
||||
await takeout.get_messages('me') # wrapped through takeout (less limits)
|
||||
|
||||
async for message in takeout.iter_messages(chat, wait_time=0):
|
||||
... # Do something with the message
|
||||
|
||||
except errors.TakeoutInitDelayError as e:
|
||||
print('Must wait', e.seconds, 'before takeout')
|
||||
"""
|
||||
request_kwargs = dict(
|
||||
contacts=contacts,
|
||||
message_users=users,
|
||||
|
@ -220,21 +138,6 @@ class AccountMethods:
|
|||
return _TakeoutClient(finalize, self, request)
|
||||
|
||||
async def end_takeout(self: 'TelegramClient', success: bool) -> bool:
|
||||
"""
|
||||
Finishes the current takeout session.
|
||||
|
||||
Arguments
|
||||
success (`bool`):
|
||||
Whether the takeout completed successfully or not.
|
||||
|
||||
Returns
|
||||
`True` if the operation was successful, `False` otherwise.
|
||||
|
||||
Example
|
||||
.. code-block:: python
|
||||
|
||||
await client.end_takeout(success=False)
|
||||
"""
|
||||
try:
|
||||
async with _TakeoutClient(True, self, None) as takeout:
|
||||
takeout.success = success
|
||||
|
|
|
@ -12,10 +12,6 @@ if typing.TYPE_CHECKING:
|
|||
from .telegramclient import TelegramClient
|
||||
|
||||
|
||||
class AuthMethods:
|
||||
|
||||
# region Public methods
|
||||
|
||||
def start(
|
||||
self: 'TelegramClient',
|
||||
phone: typing.Callable[[], str] = lambda: input('Please enter your phone (or bot token): '),
|
||||
|
@ -27,81 +23,6 @@ class AuthMethods:
|
|||
first_name: str = 'New User',
|
||||
last_name: str = '',
|
||||
max_attempts: int = 3) -> 'TelegramClient':
|
||||
"""
|
||||
Starts the client (connects and logs in if necessary).
|
||||
|
||||
By default, this method will be interactive (asking for
|
||||
user input if needed), and will handle 2FA if enabled too.
|
||||
|
||||
If the phone doesn't belong to an existing account (and will hence
|
||||
`sign_up` for a new one), **you are agreeing to Telegram's
|
||||
Terms of Service. This is required and your account
|
||||
will be banned otherwise.** See https://telegram.org/tos
|
||||
and https://core.telegram.org/api/terms.
|
||||
|
||||
If the event loop is already running, this method returns a
|
||||
coroutine that you should await on your own code; otherwise
|
||||
the loop is ran until said coroutine completes.
|
||||
|
||||
Arguments
|
||||
phone (`str` | `int` | `callable`):
|
||||
The phone (or callable without arguments to get it)
|
||||
to which the code will be sent. If a bot-token-like
|
||||
string is given, it will be used as such instead.
|
||||
The argument may be a coroutine.
|
||||
|
||||
password (`str`, `callable`, optional):
|
||||
The password for 2 Factor Authentication (2FA).
|
||||
This is only required if it is enabled in your account.
|
||||
The argument may be a coroutine.
|
||||
|
||||
bot_token (`str`):
|
||||
Bot Token obtained by `@BotFather <https://t.me/BotFather>`_
|
||||
to log in as a bot. Cannot be specified with ``phone`` (only
|
||||
one of either allowed).
|
||||
|
||||
force_sms (`bool`, optional):
|
||||
Whether to force sending the code request as SMS.
|
||||
This only makes sense when signing in with a `phone`.
|
||||
|
||||
code_callback (`callable`, optional):
|
||||
A callable that will be used to retrieve the Telegram
|
||||
login code. Defaults to `input()`.
|
||||
The argument may be a coroutine.
|
||||
|
||||
first_name (`str`, optional):
|
||||
The first name to be used if signing up. This has no
|
||||
effect if the account already exists and you sign in.
|
||||
|
||||
last_name (`str`, optional):
|
||||
Similar to the first name, but for the last. Optional.
|
||||
|
||||
max_attempts (`int`, optional):
|
||||
How many times the code/password callback should be
|
||||
retried or switching between signing in and signing up.
|
||||
|
||||
Returns
|
||||
This `TelegramClient`, so initialization
|
||||
can be chained with ``.start()``.
|
||||
|
||||
Example
|
||||
.. code-block:: python
|
||||
|
||||
client = TelegramClient('anon', api_id, api_hash)
|
||||
|
||||
# Starting as a bot account
|
||||
await client.start(bot_token=bot_token)
|
||||
|
||||
# Starting as a user account
|
||||
await client.start(phone)
|
||||
# Please enter the code you received: 12345
|
||||
# Please enter your password: *******
|
||||
# (You are now logged in)
|
||||
|
||||
# Starting using a context manager (this calls start()):
|
||||
with client:
|
||||
pass
|
||||
"""
|
||||
if code_callback is None:
|
||||
def code_callback():
|
||||
return input('Please enter the code you received: ')
|
||||
|
@ -285,55 +206,6 @@ class AuthMethods:
|
|||
password: str = None,
|
||||
bot_token: str = None,
|
||||
phone_code_hash: str = None) -> 'typing.Union[types.User, types.auth.SentCode]':
|
||||
"""
|
||||
Logs in to Telegram to an existing user or bot account.
|
||||
|
||||
You should only use this if you are not authorized yet.
|
||||
|
||||
This method will send the code if it's not provided.
|
||||
|
||||
.. note::
|
||||
|
||||
In most cases, you should simply use `start()` and not this method.
|
||||
|
||||
Arguments
|
||||
phone (`str` | `int`):
|
||||
The phone to send the code to if no code was provided,
|
||||
or to override the phone that was previously used with
|
||||
these requests.
|
||||
|
||||
code (`str` | `int`):
|
||||
The code that Telegram sent. Note that if you have sent this
|
||||
code through the application itself it will immediately
|
||||
expire. If you want to send the code, obfuscate it somehow.
|
||||
If you're not doing any of this you can ignore this note.
|
||||
|
||||
password (`str`):
|
||||
2FA password, should be used if a previous call raised
|
||||
``SessionPasswordNeededError``.
|
||||
|
||||
bot_token (`str`):
|
||||
Used to sign in as a bot. Not all requests will be available.
|
||||
This should be the hash the `@BotFather <https://t.me/BotFather>`_
|
||||
gave you.
|
||||
|
||||
phone_code_hash (`str`, optional):
|
||||
The hash returned by `send_code_request`. This can be left as
|
||||
`None` to use the last hash known for the phone to be used.
|
||||
|
||||
Returns
|
||||
The signed in user, or the information about
|
||||
:meth:`send_code_request`.
|
||||
|
||||
Example
|
||||
.. code-block:: python
|
||||
|
||||
phone = '+34 123 123 123'
|
||||
await client.sign_in(phone) # send code
|
||||
|
||||
code = input('enter code: ')
|
||||
await client.sign_in(phone, code)
|
||||
"""
|
||||
me = await self.get_me()
|
||||
if me:
|
||||
return me
|
||||
|
@ -381,48 +253,6 @@ class AuthMethods:
|
|||
*,
|
||||
phone: str = None,
|
||||
phone_code_hash: str = None) -> 'types.User':
|
||||
"""
|
||||
Signs up to Telegram as a new user account.
|
||||
|
||||
Use this if you don't have an account yet.
|
||||
|
||||
You must call `send_code_request` first.
|
||||
|
||||
**By using this method you're agreeing to Telegram's
|
||||
Terms of Service. This is required and your account
|
||||
will be banned otherwise.** See https://telegram.org/tos
|
||||
and https://core.telegram.org/api/terms.
|
||||
|
||||
Arguments
|
||||
code (`str` | `int`):
|
||||
The code sent by Telegram
|
||||
|
||||
first_name (`str`):
|
||||
The first name to be used by the new account.
|
||||
|
||||
last_name (`str`, optional)
|
||||
Optional last name.
|
||||
|
||||
phone (`str` | `int`, optional):
|
||||
The phone to sign up. This will be the last phone used by
|
||||
default (you normally don't need to set this).
|
||||
|
||||
phone_code_hash (`str`, optional):
|
||||
The hash returned by `send_code_request`. This can be left as
|
||||
`None` to use the last hash known for the phone to be used.
|
||||
|
||||
Returns
|
||||
The new created :tl:`User`.
|
||||
|
||||
Example
|
||||
.. code-block:: python
|
||||
|
||||
phone = '+34 123 123 123'
|
||||
await client.send_code_request(phone)
|
||||
|
||||
code = input('enter code: ')
|
||||
await client.sign_up(code, first_name='Anna', last_name='Banana')
|
||||
"""
|
||||
me = await self.get_me()
|
||||
if me:
|
||||
return me
|
||||
|
@ -471,7 +301,6 @@ class AuthMethods:
|
|||
def _on_login(self, user):
|
||||
"""
|
||||
Callback called whenever the login or sign up process completes.
|
||||
|
||||
Returns the input user parameter.
|
||||
"""
|
||||
self._bot = bool(user.bot)
|
||||
|
@ -485,26 +314,6 @@ class AuthMethods:
|
|||
phone: str,
|
||||
*,
|
||||
force_sms: bool = False) -> 'types.auth.SentCode':
|
||||
"""
|
||||
Sends the Telegram code needed to login to the given phone number.
|
||||
|
||||
Arguments
|
||||
phone (`str` | `int`):
|
||||
The phone to which the code will be sent.
|
||||
|
||||
force_sms (`bool`, optional):
|
||||
Whether to force sending as SMS.
|
||||
|
||||
Returns
|
||||
An instance of :tl:`SentCode`.
|
||||
|
||||
Example
|
||||
.. code-block:: python
|
||||
|
||||
phone = '+34 123 123 123'
|
||||
sent = await client.send_code_request(phone)
|
||||
print(sent)
|
||||
"""
|
||||
result = None
|
||||
phone = utils.parse_phone(phone) or self._phone
|
||||
phone_hash = self._phone_code_hash.get(phone)
|
||||
|
@ -537,55 +346,11 @@ class AuthMethods:
|
|||
return result
|
||||
|
||||
async def qr_login(self: 'TelegramClient', ignored_ids: typing.List[int] = None) -> custom.QRLogin:
|
||||
"""
|
||||
Initiates the QR login procedure.
|
||||
|
||||
Note that you must be connected before invoking this, as with any
|
||||
other request.
|
||||
|
||||
It is up to the caller to decide how to present the code to the user,
|
||||
whether it's the URL, using the token bytes directly, or generating
|
||||
a QR code and displaying it by other means.
|
||||
|
||||
See the documentation for `QRLogin` to see how to proceed after this.
|
||||
|
||||
Arguments
|
||||
ignored_ids (List[`int`]):
|
||||
List of already logged-in user IDs, to prevent logging in
|
||||
twice with the same user.
|
||||
|
||||
Returns
|
||||
An instance of `QRLogin`.
|
||||
|
||||
Example
|
||||
.. code-block:: python
|
||||
|
||||
def display_url_as_qr(url):
|
||||
pass # do whatever to show url as a qr to the user
|
||||
|
||||
qr_login = await client.qr_login()
|
||||
display_url_as_qr(qr_login.url)
|
||||
|
||||
# Important! You need to wait for the login to complete!
|
||||
await qr_login.wait()
|
||||
"""
|
||||
qr_login = custom.QRLogin(self, ignored_ids or [])
|
||||
await qr_login.recreate()
|
||||
return qr_login
|
||||
|
||||
async def log_out(self: 'TelegramClient') -> bool:
|
||||
"""
|
||||
Logs out Telegram and deletes the current ``*.session`` file.
|
||||
|
||||
Returns
|
||||
`True` if the operation was successful.
|
||||
|
||||
Example
|
||||
.. code-block:: python
|
||||
|
||||
# Note: you will need to login again!
|
||||
await client.log_out()
|
||||
"""
|
||||
try:
|
||||
await self(functions.auth.LogOutRequest())
|
||||
except errors.RPCError:
|
||||
|
@ -608,59 +373,6 @@ class AuthMethods:
|
|||
hint: str = '',
|
||||
email: str = None,
|
||||
email_code_callback: typing.Callable[[int], str] = None) -> bool:
|
||||
"""
|
||||
Changes the 2FA settings of the logged in user.
|
||||
|
||||
Review carefully the parameter explanations before using this method.
|
||||
|
||||
Note that this method may be *incredibly* slow depending on the
|
||||
prime numbers that must be used during the process to make sure
|
||||
that everything is safe.
|
||||
|
||||
Has no effect if both current and new password are omitted.
|
||||
|
||||
Arguments
|
||||
current_password (`str`, optional):
|
||||
The current password, to authorize changing to ``new_password``.
|
||||
Must be set if changing existing 2FA settings.
|
||||
Must **not** be set if 2FA is currently disabled.
|
||||
Passing this by itself will remove 2FA (if correct).
|
||||
|
||||
new_password (`str`, optional):
|
||||
The password to set as 2FA.
|
||||
If 2FA was already enabled, ``current_password`` **must** be set.
|
||||
Leaving this blank or `None` will remove the password.
|
||||
|
||||
hint (`str`, optional):
|
||||
Hint to be displayed by Telegram when it asks for 2FA.
|
||||
Leaving unspecified is highly discouraged.
|
||||
Has no effect if ``new_password`` is not set.
|
||||
|
||||
email (`str`, optional):
|
||||
Recovery and verification email. If present, you must also
|
||||
set `email_code_callback`, else it raises ``ValueError``.
|
||||
|
||||
email_code_callback (`callable`, optional):
|
||||
If an email is provided, a callback that returns the code sent
|
||||
to it must also be set. This callback may be asynchronous.
|
||||
It should return a string with the code. The length of the
|
||||
code will be passed to the callback as an input parameter.
|
||||
|
||||
If the callback returns an invalid code, it will raise
|
||||
``CodeInvalidError``.
|
||||
|
||||
Returns
|
||||
`True` if successful, `False` otherwise.
|
||||
|
||||
Example
|
||||
.. code-block:: python
|
||||
|
||||
# Setting a password for your account which didn't have
|
||||
await client.edit_2fa(new_password='I_<3_Telethon')
|
||||
|
||||
# Removing the password
|
||||
await client.edit_2fa(current_password='I_<3_Telethon')
|
||||
"""
|
||||
if new_password is None and current_password is None:
|
||||
return False
|
||||
|
||||
|
@ -704,18 +416,3 @@ class AuthMethods:
|
|||
await self(functions.account.ConfirmPasswordEmailRequest(code))
|
||||
|
||||
return True
|
||||
|
||||
# endregion
|
||||
|
||||
# region with blocks
|
||||
|
||||
async def __aenter__(self):
|
||||
return await self.start()
|
||||
|
||||
async def __aexit__(self, *args):
|
||||
await self.disconnect()
|
||||
|
||||
__enter__ = helpers._sync_enter
|
||||
__exit__ = helpers._sync_exit
|
||||
|
||||
# endregion
|
||||
|
|
|
@ -7,7 +7,6 @@ if typing.TYPE_CHECKING:
|
|||
from .telegramclient import TelegramClient
|
||||
|
||||
|
||||
class BotMethods:
|
||||
async def inline_query(
|
||||
self: 'TelegramClient',
|
||||
bot: 'hints.EntityLike',
|
||||
|
@ -16,45 +15,6 @@ class BotMethods:
|
|||
entity: 'hints.EntityLike' = None,
|
||||
offset: str = None,
|
||||
geo_point: 'types.GeoPoint' = None) -> custom.InlineResults:
|
||||
"""
|
||||
Makes an inline query to the specified bot (``@vote New Poll``).
|
||||
|
||||
Arguments
|
||||
bot (`entity`):
|
||||
The bot entity to which the inline query should be made.
|
||||
|
||||
query (`str`):
|
||||
The query that should be made to the bot.
|
||||
|
||||
entity (`entity`, optional):
|
||||
The entity where the inline query is being made from. Certain
|
||||
bots use this to display different results depending on where
|
||||
it's used, such as private chats, groups or channels.
|
||||
|
||||
If specified, it will also be the default entity where the
|
||||
message will be sent after clicked. Otherwise, the "empty
|
||||
peer" will be used, which some bots may not handle correctly.
|
||||
|
||||
offset (`str`, optional):
|
||||
The string offset to use for the bot.
|
||||
|
||||
geo_point (:tl:`GeoPoint`, optional)
|
||||
The geo point location information to send to the bot
|
||||
for localised results. Available under some bots.
|
||||
|
||||
Returns
|
||||
A list of `custom.InlineResult
|
||||
<telethon.tl.custom.inlineresult.InlineResult>`.
|
||||
|
||||
Example
|
||||
.. code-block:: python
|
||||
|
||||
# Make an inline query to @like
|
||||
results = await client.inline_query('like', 'Do you like Telethon?')
|
||||
|
||||
# Send the first result to some chat
|
||||
message = await results[0].click('TelethonOffTopic')
|
||||
"""
|
||||
bot = await self.get_input_entity(bot)
|
||||
if entity:
|
||||
peer = await self.get_input_entity(entity)
|
||||
|
|
|
@ -4,40 +4,9 @@ from .. import utils, hints
|
|||
from ..tl import types, custom
|
||||
|
||||
|
||||
class ButtonMethods:
|
||||
@staticmethod
|
||||
def build_reply_markup(
|
||||
buttons: 'typing.Optional[hints.MarkupLike]',
|
||||
inline_only: bool = False) -> 'typing.Optional[types.TypeReplyMarkup]':
|
||||
"""
|
||||
Builds a :tl:`ReplyInlineMarkup` or :tl:`ReplyKeyboardMarkup` for
|
||||
the given buttons.
|
||||
|
||||
Does nothing if either no buttons are provided or the provided
|
||||
argument is already a reply markup.
|
||||
|
||||
You should consider using this method if you are going to reuse
|
||||
the markup very often. Otherwise, it is not necessary.
|
||||
|
||||
This method is **not** asynchronous (don't use ``await`` on it).
|
||||
|
||||
Arguments
|
||||
buttons (`hints.MarkupLike`):
|
||||
The button, list of buttons, array of buttons or markup
|
||||
to convert into a markup.
|
||||
|
||||
inline_only (`bool`, optional):
|
||||
Whether the buttons **must** be inline buttons only or not.
|
||||
|
||||
Example
|
||||
.. code-block:: python
|
||||
|
||||
from telethon import Button
|
||||
|
||||
markup = client.build_reply_markup(Button.inline('hi'))
|
||||
# later
|
||||
await client.send_message(chat, 'click me', buttons=markup)
|
||||
"""
|
||||
if buttons is None:
|
||||
return None
|
||||
|
||||
|
|
|
@ -404,10 +404,6 @@ class _ProfilePhotoIter(RequestIter):
|
|||
self.request.offset_id = result.messages[-1].id
|
||||
|
||||
|
||||
class ChatMethods:
|
||||
|
||||
# region Public methods
|
||||
|
||||
def iter_participants(
|
||||
self: 'TelegramClient',
|
||||
entity: 'hints.EntityLike',
|
||||
|
@ -416,67 +412,6 @@ class ChatMethods:
|
|||
search: str = '',
|
||||
filter: 'types.TypeChannelParticipantsFilter' = None,
|
||||
aggressive: bool = False) -> _ParticipantsIter:
|
||||
"""
|
||||
Iterator over the participants belonging to the specified chat.
|
||||
|
||||
The order is unspecified.
|
||||
|
||||
Arguments
|
||||
entity (`entity`):
|
||||
The entity from which to retrieve the participants list.
|
||||
|
||||
limit (`int`):
|
||||
Limits amount of participants fetched.
|
||||
|
||||
search (`str`, optional):
|
||||
Look for participants with this string in name/username.
|
||||
|
||||
If ``aggressive is True``, the symbols from this string will
|
||||
be used.
|
||||
|
||||
filter (:tl:`ChannelParticipantsFilter`, optional):
|
||||
The filter to be used, if you want e.g. only admins
|
||||
Note that you might not have permissions for some filter.
|
||||
This has no effect for normal chats or users.
|
||||
|
||||
.. note::
|
||||
|
||||
The filter :tl:`ChannelParticipantsBanned` will return
|
||||
*restricted* users. If you want *banned* users you should
|
||||
use :tl:`ChannelParticipantsKicked` instead.
|
||||
|
||||
aggressive (`bool`, optional):
|
||||
Aggressively looks for all participants in the chat.
|
||||
|
||||
This is useful for channels since 20 July 2018,
|
||||
Telegram added a server-side limit where only the
|
||||
first 200 members can be retrieved. With this flag
|
||||
set, more than 200 will be often be retrieved.
|
||||
|
||||
This has no effect if a ``filter`` is given.
|
||||
|
||||
Yields
|
||||
The :tl:`User` objects returned by :tl:`GetParticipantsRequest`
|
||||
with an additional ``.participant`` attribute which is the
|
||||
matched :tl:`ChannelParticipant` type for channels/megagroups
|
||||
or :tl:`ChatParticipants` for normal chats.
|
||||
|
||||
Example
|
||||
.. code-block:: python
|
||||
|
||||
# Show all user IDs in a chat
|
||||
async for user in client.iter_participants(chat):
|
||||
print(user.id)
|
||||
|
||||
# Search by name
|
||||
async for user in client.iter_participants(chat, search='name'):
|
||||
print(user.username)
|
||||
|
||||
# Filter by admins
|
||||
from telethon.tl.types import ChannelParticipantsAdmins
|
||||
async for user in client.iter_participants(chat, filter=ChannelParticipantsAdmins):
|
||||
print(user.first_name)
|
||||
"""
|
||||
return _ParticipantsIter(
|
||||
self,
|
||||
limit,
|
||||
|
@ -490,24 +425,8 @@ class ChatMethods:
|
|||
self: 'TelegramClient',
|
||||
*args,
|
||||
**kwargs) -> 'hints.TotalList':
|
||||
"""
|
||||
Same as `iter_participants()`, but returns a
|
||||
`TotalList <telethon.helpers.TotalList>` instead.
|
||||
|
||||
Example
|
||||
.. code-block:: python
|
||||
|
||||
users = await client.get_participants(chat)
|
||||
print(users[0].first_name)
|
||||
|
||||
for user in users:
|
||||
if user.username is not None:
|
||||
print(user.username)
|
||||
"""
|
||||
return await self.iter_participants(*args, **kwargs).collect()
|
||||
|
||||
get_participants.__signature__ = inspect.signature(iter_participants)
|
||||
|
||||
|
||||
def iter_admin_log(
|
||||
self: 'TelegramClient',
|
||||
|
@ -533,105 +452,6 @@ class ChatMethods:
|
|||
edit: bool = None,
|
||||
delete: bool = None,
|
||||
group_call: bool = None) -> _AdminLogIter:
|
||||
"""
|
||||
Iterator over the admin log for the specified channel.
|
||||
|
||||
The default order is from the most recent event to to the oldest.
|
||||
|
||||
Note that you must be an administrator of it to use this method.
|
||||
|
||||
If none of the filters are present (i.e. they all are `None`),
|
||||
*all* event types will be returned. If at least one of them is
|
||||
`True`, only those that are true will be returned.
|
||||
|
||||
Arguments
|
||||
entity (`entity`):
|
||||
The channel entity from which to get its admin log.
|
||||
|
||||
limit (`int` | `None`, optional):
|
||||
Number of events to be retrieved.
|
||||
|
||||
The limit may also be `None`, which would eventually return
|
||||
the whole history.
|
||||
|
||||
max_id (`int`):
|
||||
All the events with a higher (newer) ID or equal to this will
|
||||
be excluded.
|
||||
|
||||
min_id (`int`):
|
||||
All the events with a lower (older) ID or equal to this will
|
||||
be excluded.
|
||||
|
||||
search (`str`):
|
||||
The string to be used as a search query.
|
||||
|
||||
admins (`entity` | `list`):
|
||||
If present, the events will be filtered by these admins
|
||||
(or single admin) and only those caused by them will be
|
||||
returned.
|
||||
|
||||
join (`bool`):
|
||||
If `True`, events for when a user joined will be returned.
|
||||
|
||||
leave (`bool`):
|
||||
If `True`, events for when a user leaves will be returned.
|
||||
|
||||
invite (`bool`):
|
||||
If `True`, events for when a user joins through an invite
|
||||
link will be returned.
|
||||
|
||||
restrict (`bool`):
|
||||
If `True`, events with partial restrictions will be
|
||||
returned. This is what the API calls "ban".
|
||||
|
||||
unrestrict (`bool`):
|
||||
If `True`, events removing restrictions will be returned.
|
||||
This is what the API calls "unban".
|
||||
|
||||
ban (`bool`):
|
||||
If `True`, events applying or removing all restrictions will
|
||||
be returned. This is what the API calls "kick" (restricting
|
||||
all permissions removed is a ban, which kicks the user).
|
||||
|
||||
unban (`bool`):
|
||||
If `True`, events removing all restrictions will be
|
||||
returned. This is what the API calls "unkick".
|
||||
|
||||
promote (`bool`):
|
||||
If `True`, events with admin promotions will be returned.
|
||||
|
||||
demote (`bool`):
|
||||
If `True`, events with admin demotions will be returned.
|
||||
|
||||
info (`bool`):
|
||||
If `True`, events changing the group info will be returned.
|
||||
|
||||
settings (`bool`):
|
||||
If `True`, events changing the group settings will be
|
||||
returned.
|
||||
|
||||
pinned (`bool`):
|
||||
If `True`, events of new pinned messages will be returned.
|
||||
|
||||
edit (`bool`):
|
||||
If `True`, events of message edits will be returned.
|
||||
|
||||
delete (`bool`):
|
||||
If `True`, events of message deletions will be returned.
|
||||
|
||||
group_call (`bool`):
|
||||
If `True`, events related to group calls will be returned.
|
||||
|
||||
Yields
|
||||
Instances of `AdminLogEvent <telethon.tl.custom.adminlogevent.AdminLogEvent>`.
|
||||
|
||||
Example
|
||||
.. code-block:: python
|
||||
|
||||
async for event in client.iter_admin_log(channel):
|
||||
if event.changed_title:
|
||||
print('The title changed from', event.old, 'to', event.new)
|
||||
"""
|
||||
return _AdminLogIter(
|
||||
self,
|
||||
limit,
|
||||
|
@ -661,21 +481,8 @@ class ChatMethods:
|
|||
self: 'TelegramClient',
|
||||
*args,
|
||||
**kwargs) -> 'hints.TotalList':
|
||||
"""
|
||||
Same as `iter_admin_log()`, but returns a ``list`` instead.
|
||||
|
||||
Example
|
||||
.. code-block:: python
|
||||
|
||||
# Get a list of deleted message events which said "heck"
|
||||
events = await client.get_admin_log(channel, search='heck', delete=True)
|
||||
|
||||
# Print the old message before it was deleted
|
||||
print(events[0].old)
|
||||
"""
|
||||
return await self.iter_admin_log(*args, **kwargs).collect()
|
||||
|
||||
get_admin_log.__signature__ = inspect.signature(iter_admin_log)
|
||||
|
||||
def iter_profile_photos(
|
||||
self: 'TelegramClient',
|
||||
|
@ -684,37 +491,6 @@ class ChatMethods:
|
|||
*,
|
||||
offset: int = 0,
|
||||
max_id: int = 0) -> _ProfilePhotoIter:
|
||||
"""
|
||||
Iterator over a user's profile photos or a chat's photos.
|
||||
|
||||
The order is from the most recent photo to the oldest.
|
||||
|
||||
Arguments
|
||||
entity (`entity`):
|
||||
The entity from which to get the profile or chat photos.
|
||||
|
||||
limit (`int` | `None`, optional):
|
||||
Number of photos to be retrieved.
|
||||
|
||||
The limit may also be `None`, which would eventually all
|
||||
the photos that are still available.
|
||||
|
||||
offset (`int`):
|
||||
How many photos should be skipped before returning the first one.
|
||||
|
||||
max_id (`int`):
|
||||
The maximum ID allowed when fetching photos.
|
||||
|
||||
Yields
|
||||
Instances of :tl:`Photo`.
|
||||
|
||||
Example
|
||||
.. code-block:: python
|
||||
|
||||
# Download all the profile photos of some user
|
||||
async for photo in client.iter_profile_photos(user):
|
||||
await client.download_media(photo)
|
||||
"""
|
||||
return _ProfilePhotoIter(
|
||||
self,
|
||||
limit,
|
||||
|
@ -727,22 +503,8 @@ class ChatMethods:
|
|||
self: 'TelegramClient',
|
||||
*args,
|
||||
**kwargs) -> 'hints.TotalList':
|
||||
"""
|
||||
Same as `iter_profile_photos()`, but returns a
|
||||
`TotalList <telethon.helpers.TotalList>` instead.
|
||||
|
||||
Example
|
||||
.. code-block:: python
|
||||
|
||||
# Get the photos of a channel
|
||||
photos = await client.get_profile_photos(channel)
|
||||
|
||||
# Download the oldest photo
|
||||
await client.download_media(photos[-1])
|
||||
"""
|
||||
return await self.iter_profile_photos(*args, **kwargs).collect()
|
||||
|
||||
get_profile_photos.__signature__ = inspect.signature(iter_profile_photos)
|
||||
|
||||
def action(
|
||||
self: 'TelegramClient',
|
||||
|
@ -751,75 +513,6 @@ class ChatMethods:
|
|||
*,
|
||||
delay: float = 4,
|
||||
auto_cancel: bool = True) -> 'typing.Union[_ChatAction, typing.Coroutine]':
|
||||
"""
|
||||
Returns a context-manager object to represent a "chat action".
|
||||
|
||||
Chat actions indicate things like "user is typing", "user is
|
||||
uploading a photo", etc.
|
||||
|
||||
If the action is ``'cancel'``, you should just ``await`` the result,
|
||||
since it makes no sense to use a context-manager for it.
|
||||
|
||||
See the example below for intended usage.
|
||||
|
||||
Arguments
|
||||
entity (`entity`):
|
||||
The entity where the action should be showed in.
|
||||
|
||||
action (`str` | :tl:`SendMessageAction`):
|
||||
The action to show. You can either pass a instance of
|
||||
:tl:`SendMessageAction` or better, a string used while:
|
||||
|
||||
* ``'typing'``: typing a text message.
|
||||
* ``'contact'``: choosing a contact.
|
||||
* ``'game'``: playing a game.
|
||||
* ``'location'``: choosing a geo location.
|
||||
* ``'sticker'``: choosing a sticker.
|
||||
* ``'record-audio'``: recording a voice note.
|
||||
You may use ``'record-voice'`` as alias.
|
||||
* ``'record-round'``: recording a round video.
|
||||
* ``'record-video'``: recording a normal video.
|
||||
* ``'audio'``: sending an audio file (voice note or song).
|
||||
You may use ``'voice'`` and ``'song'`` as aliases.
|
||||
* ``'round'``: uploading a round video.
|
||||
* ``'video'``: uploading a video file.
|
||||
* ``'photo'``: uploading a photo.
|
||||
* ``'document'``: uploading a document file.
|
||||
You may use ``'file'`` as alias.
|
||||
* ``'cancel'``: cancel any pending action in this chat.
|
||||
|
||||
Invalid strings will raise a ``ValueError``.
|
||||
|
||||
delay (`int` | `float`):
|
||||
The delay, in seconds, to wait between sending actions.
|
||||
For example, if the delay is 5 and it takes 7 seconds to
|
||||
do something, three requests will be made at 0s, 5s, and
|
||||
7s to cancel the action.
|
||||
|
||||
auto_cancel (`bool`):
|
||||
Whether the action should be cancelled once the context
|
||||
manager exists or not. The default is `True`, since
|
||||
you don't want progress to be shown when it has already
|
||||
completed.
|
||||
|
||||
Returns
|
||||
Either a context-manager object or a coroutine.
|
||||
|
||||
Example
|
||||
.. code-block:: python
|
||||
|
||||
# Type for 2 seconds, then send a message
|
||||
async with client.action(chat, 'typing'):
|
||||
await asyncio.sleep(2)
|
||||
await client.send_message(chat, 'Hello world! I type slow ^^')
|
||||
|
||||
# Cancel any previous action
|
||||
await client.action(chat, 'cancel')
|
||||
|
||||
# Upload a document, showing its progress (most clients ignore this)
|
||||
async with client.action(chat, 'document') as action:
|
||||
await client.send_file(chat, zip_file, progress_callback=action.progress)
|
||||
"""
|
||||
if isinstance(action, str):
|
||||
try:
|
||||
action = _ChatAction._str_mapping[action.lower()]
|
||||
|
@ -858,93 +551,6 @@ class ChatMethods:
|
|||
anonymous: bool = None,
|
||||
is_admin: bool = None,
|
||||
title: str = None) -> types.Updates:
|
||||
"""
|
||||
Edits admin permissions for someone in a chat.
|
||||
|
||||
Raises an error if a wrong combination of rights are given
|
||||
(e.g. you don't have enough permissions to grant one).
|
||||
|
||||
Unless otherwise stated, permissions will work in channels and megagroups.
|
||||
|
||||
Arguments
|
||||
entity (`entity`):
|
||||
The channel, megagroup or chat where the promotion should happen.
|
||||
|
||||
user (`entity`):
|
||||
The user to be promoted.
|
||||
|
||||
change_info (`bool`, optional):
|
||||
Whether the user will be able to change info.
|
||||
|
||||
post_messages (`bool`, optional):
|
||||
Whether the user will be able to post in the channel.
|
||||
This will only work in broadcast channels.
|
||||
|
||||
edit_messages (`bool`, optional):
|
||||
Whether the user will be able to edit messages in the channel.
|
||||
This will only work in broadcast channels.
|
||||
|
||||
delete_messages (`bool`, optional):
|
||||
Whether the user will be able to delete messages.
|
||||
|
||||
ban_users (`bool`, optional):
|
||||
Whether the user will be able to ban users.
|
||||
|
||||
invite_users (`bool`, optional):
|
||||
Whether the user will be able to invite users. Needs some testing.
|
||||
|
||||
pin_messages (`bool`, optional):
|
||||
Whether the user will be able to pin messages.
|
||||
|
||||
add_admins (`bool`, optional):
|
||||
Whether the user will be able to add admins.
|
||||
|
||||
manage_call (`bool`, optional):
|
||||
Whether the user will be able to manage group calls.
|
||||
|
||||
anonymous (`bool`, optional):
|
||||
Whether the user will remain anonymous when sending messages.
|
||||
The sender of the anonymous messages becomes the group itself.
|
||||
|
||||
.. note::
|
||||
|
||||
Users may be able to identify the anonymous admin by its
|
||||
custom title, so additional care is needed when using both
|
||||
``anonymous`` and custom titles. For example, if multiple
|
||||
anonymous admins share the same title, users won't be able
|
||||
to distinguish them.
|
||||
|
||||
is_admin (`bool`, optional):
|
||||
Whether the user will be an admin in the chat.
|
||||
This will only work in small group chats.
|
||||
Whether the user will be an admin in the chat. This is the
|
||||
only permission available in small group chats, and when
|
||||
used in megagroups, all non-explicitly set permissions will
|
||||
have this value.
|
||||
|
||||
Essentially, only passing ``is_admin=True`` will grant all
|
||||
permissions, but you can still disable those you need.
|
||||
|
||||
title (`str`, optional):
|
||||
The custom title (also known as "rank") to show for this admin.
|
||||
This text will be shown instead of the "admin" badge.
|
||||
This will only work in channels and megagroups.
|
||||
|
||||
When left unspecified or empty, the default localized "admin"
|
||||
badge will be shown.
|
||||
|
||||
Returns
|
||||
The resulting :tl:`Updates` object.
|
||||
|
||||
Example
|
||||
.. code-block:: python
|
||||
|
||||
# Allowing `user` to pin messages in `chat`
|
||||
await client.edit_admin(chat, user, pin_messages=True)
|
||||
|
||||
# Granting all permissions except for `add_admins`
|
||||
await client.edit_admin(chat, user, is_admin=True, add_admins=False)
|
||||
"""
|
||||
entity = await self.get_input_entity(entity)
|
||||
user = await self.get_input_entity(user)
|
||||
ty = helpers._entity_type(user)
|
||||
|
@ -1011,103 +617,6 @@ class ChatMethods:
|
|||
change_info: bool = True,
|
||||
invite_users: bool = True,
|
||||
pin_messages: bool = True) -> types.Updates:
|
||||
"""
|
||||
Edits user restrictions in a chat.
|
||||
|
||||
Set an argument to `False` to apply a restriction (i.e. remove
|
||||
the permission), or omit them to use the default `True` (i.e.
|
||||
don't apply a restriction).
|
||||
|
||||
Raises an error if a wrong combination of rights are given
|
||||
(e.g. you don't have enough permissions to revoke one).
|
||||
|
||||
By default, each boolean argument is `True`, meaning that it
|
||||
is true that the user has access to the default permission
|
||||
and may be able to make use of it.
|
||||
|
||||
If you set an argument to `False`, then a restriction is applied
|
||||
regardless of the default permissions.
|
||||
|
||||
It is important to note that `True` does *not* mean grant, only
|
||||
"don't restrict", and this is where the default permissions come
|
||||
in. A user may have not been revoked the ``pin_messages`` permission
|
||||
(it is `True`) but they won't be able to use it if the default
|
||||
permissions don't allow it either.
|
||||
|
||||
Arguments
|
||||
entity (`entity`):
|
||||
The channel or megagroup where the restriction should happen.
|
||||
|
||||
user (`entity`, optional):
|
||||
If specified, the permission will be changed for the specific user.
|
||||
If left as `None`, the default chat permissions will be updated.
|
||||
|
||||
until_date (`DateLike`, optional):
|
||||
When the user will be unbanned.
|
||||
|
||||
If the due date or duration is longer than 366 days or shorter than
|
||||
30 seconds, the ban will be forever. Defaults to ``0`` (ban forever).
|
||||
|
||||
view_messages (`bool`, optional):
|
||||
Whether the user is able to view messages or not.
|
||||
Forbidding someone from viewing messages equals to banning them.
|
||||
This will only work if ``user`` is set.
|
||||
|
||||
send_messages (`bool`, optional):
|
||||
Whether the user is able to send messages or not.
|
||||
|
||||
send_media (`bool`, optional):
|
||||
Whether the user is able to send media or not.
|
||||
|
||||
send_stickers (`bool`, optional):
|
||||
Whether the user is able to send stickers or not.
|
||||
|
||||
send_gifs (`bool`, optional):
|
||||
Whether the user is able to send animated gifs or not.
|
||||
|
||||
send_games (`bool`, optional):
|
||||
Whether the user is able to send games or not.
|
||||
|
||||
send_inline (`bool`, optional):
|
||||
Whether the user is able to use inline bots or not.
|
||||
|
||||
embed_link_previews (`bool`, optional):
|
||||
Whether the user is able to enable the link preview in the
|
||||
messages they send. Note that the user will still be able to
|
||||
send messages with links if this permission is removed, but
|
||||
these links won't display a link preview.
|
||||
|
||||
send_polls (`bool`, optional):
|
||||
Whether the user is able to send polls or not.
|
||||
|
||||
change_info (`bool`, optional):
|
||||
Whether the user is able to change info or not.
|
||||
|
||||
invite_users (`bool`, optional):
|
||||
Whether the user is able to invite other users or not.
|
||||
|
||||
pin_messages (`bool`, optional):
|
||||
Whether the user is able to pin messages or not.
|
||||
|
||||
Returns
|
||||
The resulting :tl:`Updates` object.
|
||||
|
||||
Example
|
||||
.. code-block:: python
|
||||
|
||||
from datetime import timedelta
|
||||
|
||||
# Banning `user` from `chat` for 1 minute
|
||||
await client.edit_permissions(chat, user, timedelta(minutes=1),
|
||||
view_messages=False)
|
||||
|
||||
# Banning `user` from `chat` forever
|
||||
await client.edit_permissions(chat, user, view_messages=False)
|
||||
|
||||
# Kicking someone (ban + un-ban)
|
||||
await client.edit_permissions(chat, user, view_messages=False)
|
||||
await client.edit_permissions(chat, user)
|
||||
"""
|
||||
entity = await self.get_input_entity(entity)
|
||||
ty = helpers._entity_type(entity)
|
||||
if ty != helpers._EntityType.CHANNEL:
|
||||
|
@ -1154,38 +663,6 @@ class ChatMethods:
|
|||
entity: 'hints.EntityLike',
|
||||
user: 'typing.Optional[hints.EntityLike]'
|
||||
):
|
||||
"""
|
||||
Kicks a user from a chat.
|
||||
|
||||
Kicking yourself (``'me'``) will result in leaving the chat.
|
||||
|
||||
.. note::
|
||||
|
||||
Attempting to kick someone who was banned will remove their
|
||||
restrictions (and thus unbanning them), since kicking is just
|
||||
ban + unban.
|
||||
|
||||
Arguments
|
||||
entity (`entity`):
|
||||
The channel or chat where the user should be kicked from.
|
||||
|
||||
user (`entity`, optional):
|
||||
The user to kick.
|
||||
|
||||
Returns
|
||||
Returns the service `Message <telethon.tl.custom.message.Message>`
|
||||
produced about a user being kicked, if any.
|
||||
|
||||
Example
|
||||
.. code-block:: python
|
||||
|
||||
# Kick some user from some chat, and deleting the service message
|
||||
msg = await client.kick_participant(chat, user)
|
||||
await msg.delete()
|
||||
|
||||
# Leaving chat
|
||||
await client.kick_participant(chat, 'me')
|
||||
"""
|
||||
entity = await self.get_input_entity(entity)
|
||||
user = await self.get_input_entity(user)
|
||||
if helpers._entity_type(user) != helpers._EntityType.USER:
|
||||
|
@ -1222,37 +699,6 @@ class ChatMethods:
|
|||
entity: 'hints.EntityLike',
|
||||
user: 'hints.EntityLike' = None
|
||||
) -> 'typing.Optional[custom.ParticipantPermissions]':
|
||||
"""
|
||||
Fetches the permissions of a user in a specific chat or channel or
|
||||
get Default Restricted Rights of Chat or Channel.
|
||||
|
||||
.. note::
|
||||
|
||||
This request has to fetch the entire chat for small group chats,
|
||||
which can get somewhat expensive, so use of a cache is advised.
|
||||
|
||||
Arguments
|
||||
entity (`entity`):
|
||||
The channel or chat the user is participant of.
|
||||
|
||||
user (`entity`, optional):
|
||||
Target user.
|
||||
|
||||
Returns
|
||||
A `ParticipantPermissions <telethon.tl.custom.participantpermissions.ParticipantPermissions>`
|
||||
instance. Refer to its documentation to see what properties are
|
||||
available.
|
||||
|
||||
Example
|
||||
.. code-block:: python
|
||||
|
||||
permissions = await client.get_permissions(chat, user)
|
||||
if permissions.is_admin:
|
||||
# do something
|
||||
|
||||
# Get Banned Permissions of Chat
|
||||
await client.get_permissions(chat)
|
||||
"""
|
||||
entity = await self.get_entity(entity)
|
||||
|
||||
if not user:
|
||||
|
@ -1292,45 +738,6 @@ class ChatMethods:
|
|||
entity: 'hints.EntityLike',
|
||||
message: 'typing.Union[int, types.Message]' = None,
|
||||
):
|
||||
"""
|
||||
Retrieves statistics from the given megagroup or broadcast channel.
|
||||
|
||||
Note that some restrictions apply before being able to fetch statistics,
|
||||
in particular the channel must have enough members (for megagroups, this
|
||||
requires `at least 500 members`_).
|
||||
|
||||
Arguments
|
||||
entity (`entity`):
|
||||
The channel from which to get statistics.
|
||||
|
||||
message (`int` | ``Message``, optional):
|
||||
The message ID from which to get statistics, if your goal is
|
||||
to obtain the statistics of a single message.
|
||||
|
||||
Raises
|
||||
If the given entity is not a channel (broadcast or megagroup),
|
||||
a `TypeError` is raised.
|
||||
|
||||
If there are not enough members (poorly named) errors such as
|
||||
``telethon.errors.ChatAdminRequiredError`` will appear.
|
||||
|
||||
Returns
|
||||
If both ``entity`` and ``message`` were provided, returns
|
||||
:tl:`MessageStats`. Otherwise, either :tl:`BroadcastStats` or
|
||||
:tl:`MegagroupStats`, depending on whether the input belonged to a
|
||||
broadcast channel or megagroup.
|
||||
|
||||
Example
|
||||
.. code-block:: python
|
||||
|
||||
# Some megagroup or channel username or ID to fetch
|
||||
channel = -100123
|
||||
stats = await client.get_stats(channel)
|
||||
print('Stats from', stats.period.min_date, 'to', stats.period.max_date, ':')
|
||||
print(stats.stringify())
|
||||
|
||||
.. _`at least 500 members`: https://telegram.org/blog/profile-videos-people-nearby-and-more
|
||||
"""
|
||||
entity = await self.get_input_entity(entity)
|
||||
if helpers._entity_type(entity) != helpers._EntityType.CHANNEL:
|
||||
raise TypeError('You must pass a channel entity')
|
||||
|
@ -1364,5 +771,3 @@ class ChatMethods:
|
|||
return await sender.send(req)
|
||||
finally:
|
||||
await self._return_exported_sender(sender)
|
||||
|
||||
# endregion
|
||||
|
|
|
@ -136,10 +136,6 @@ class _DraftsIter(RequestIter):
|
|||
return []
|
||||
|
||||
|
||||
class DialogMethods:
|
||||
|
||||
# region Public methods
|
||||
|
||||
def iter_dialogs(
|
||||
self: 'TelegramClient',
|
||||
limit: float = None,
|
||||
|
@ -152,69 +148,6 @@ class DialogMethods:
|
|||
folder: int = None,
|
||||
archived: bool = None
|
||||
) -> _DialogsIter:
|
||||
"""
|
||||
Iterator over the dialogs (open conversations/subscribed channels).
|
||||
|
||||
The order is the same as the one seen in official applications
|
||||
(first pinned, them from those with the most recent message to
|
||||
those with the oldest message).
|
||||
|
||||
Arguments
|
||||
limit (`int` | `None`):
|
||||
How many dialogs to be retrieved as maximum. Can be set to
|
||||
`None` to retrieve all dialogs. Note that this may take
|
||||
whole minutes if you have hundreds of dialogs, as Telegram
|
||||
will tell the library to slow down through a
|
||||
``FloodWaitError``.
|
||||
|
||||
offset_date (`datetime`, optional):
|
||||
The offset date to be used.
|
||||
|
||||
offset_id (`int`, optional):
|
||||
The message ID to be used as an offset.
|
||||
|
||||
offset_peer (:tl:`InputPeer`, optional):
|
||||
The peer to be used as an offset.
|
||||
|
||||
ignore_pinned (`bool`, optional):
|
||||
Whether pinned dialogs should be ignored or not.
|
||||
When set to `True`, these won't be yielded at all.
|
||||
|
||||
ignore_migrated (`bool`, optional):
|
||||
Whether :tl:`Chat` that have ``migrated_to`` a :tl:`Channel`
|
||||
should be included or not. By default all the chats in your
|
||||
dialogs are returned, but setting this to `True` will ignore
|
||||
(i.e. skip) them in the same way official applications do.
|
||||
|
||||
folder (`int`, optional):
|
||||
The folder from which the dialogs should be retrieved.
|
||||
|
||||
If left unspecified, all dialogs (including those from
|
||||
folders) will be returned.
|
||||
|
||||
If set to ``0``, all dialogs that don't belong to any
|
||||
folder will be returned.
|
||||
|
||||
If set to a folder number like ``1``, only those from
|
||||
said folder will be returned.
|
||||
|
||||
By default Telegram assigns the folder ID ``1`` to
|
||||
archived chats, so you should use that if you need
|
||||
to fetch the archived dialogs.
|
||||
|
||||
archived (`bool`, optional):
|
||||
Alias for `folder`. If unspecified, all will be returned,
|
||||
`False` implies ``folder=0`` and `True` implies ``folder=1``.
|
||||
Yields
|
||||
Instances of `Dialog <telethon.tl.custom.dialog.Dialog>`.
|
||||
|
||||
Example
|
||||
.. code-block:: python
|
||||
|
||||
# Print all dialog IDs and the title, nicely formatted
|
||||
async for dialog in client.iter_dialogs():
|
||||
print('{:>14}: {}'.format(dialog.id, dialog.title))
|
||||
"""
|
||||
if archived is not None:
|
||||
folder = 1 if archived else 0
|
||||
|
||||
|
@ -230,61 +163,13 @@ class DialogMethods:
|
|||
)
|
||||
|
||||
async def get_dialogs(self: 'TelegramClient', *args, **kwargs) -> 'hints.TotalList':
|
||||
"""
|
||||
Same as `iter_dialogs()`, but returns a
|
||||
`TotalList <telethon.helpers.TotalList>` instead.
|
||||
|
||||
Example
|
||||
.. code-block:: python
|
||||
|
||||
# Get all open conversation, print the title of the first
|
||||
dialogs = await client.get_dialogs()
|
||||
first = dialogs[0]
|
||||
print(first.title)
|
||||
|
||||
# Use the dialog somewhere else
|
||||
await client.send_message(first, 'hi')
|
||||
|
||||
# Getting only non-archived dialogs (both equivalent)
|
||||
non_archived = await client.get_dialogs(folder=0)
|
||||
non_archived = await client.get_dialogs(archived=False)
|
||||
|
||||
# Getting only archived dialogs (both equivalent)
|
||||
archived = await client.get_dialogs(folder=1)
|
||||
archived = await client.get_dialogs(archived=True)
|
||||
"""
|
||||
return await self.iter_dialogs(*args, **kwargs).collect()
|
||||
|
||||
get_dialogs.__signature__ = inspect.signature(iter_dialogs)
|
||||
|
||||
def iter_drafts(
|
||||
self: 'TelegramClient',
|
||||
entity: 'hints.EntitiesLike' = None
|
||||
) -> _DraftsIter:
|
||||
"""
|
||||
Iterator over draft messages.
|
||||
|
||||
The order is unspecified.
|
||||
|
||||
Arguments
|
||||
entity (`hints.EntitiesLike`, optional):
|
||||
The entity or entities for which to fetch the draft messages.
|
||||
If left unspecified, all draft messages will be returned.
|
||||
|
||||
Yields
|
||||
Instances of `Draft <telethon.tl.custom.draft.Draft>`.
|
||||
|
||||
Example
|
||||
.. code-block:: python
|
||||
|
||||
# Clear all drafts
|
||||
async for draft in client.get_drafts():
|
||||
await draft.delete()
|
||||
|
||||
# Getting the drafts with 'bot1' and 'bot2'
|
||||
async for draft in client.iter_drafts(['bot1', 'bot2']):
|
||||
print(draft.text)
|
||||
"""
|
||||
if entity and not utils.is_list_like(entity):
|
||||
entity = (entity,)
|
||||
|
||||
|
@ -295,20 +180,6 @@ class DialogMethods:
|
|||
self: 'TelegramClient',
|
||||
entity: 'hints.EntitiesLike' = None
|
||||
) -> 'hints.TotalList':
|
||||
"""
|
||||
Same as `iter_drafts()`, but returns a list instead.
|
||||
|
||||
Example
|
||||
.. code-block:: python
|
||||
|
||||
# Get drafts, print the text of the first
|
||||
drafts = await client.get_drafts()
|
||||
print(drafts[0].text)
|
||||
|
||||
# Get the draft in your chat
|
||||
draft = await client.get_drafts('me')
|
||||
print(drafts.text)
|
||||
"""
|
||||
items = await self.iter_drafts(entity).collect()
|
||||
if not entity or utils.is_list_like(entity):
|
||||
return items
|
||||
|
@ -322,56 +193,6 @@ class DialogMethods:
|
|||
*,
|
||||
unpack=None
|
||||
) -> types.Updates:
|
||||
"""
|
||||
Edits the folder used by one or more dialogs to archive them.
|
||||
|
||||
Arguments
|
||||
entity (entities):
|
||||
The entity or list of entities to move to the desired
|
||||
archive folder.
|
||||
|
||||
folder (`int`):
|
||||
The folder to which the dialog should be archived to.
|
||||
|
||||
If you want to "archive" a dialog, use ``folder=1``.
|
||||
|
||||
If you want to "un-archive" it, use ``folder=0``.
|
||||
|
||||
You may also pass a list with the same length as
|
||||
`entities` if you want to control where each entity
|
||||
will go.
|
||||
|
||||
unpack (`int`, optional):
|
||||
If you want to unpack an archived folder, set this
|
||||
parameter to the folder number that you want to
|
||||
delete.
|
||||
|
||||
When you unpack a folder, all the dialogs inside are
|
||||
moved to the folder number 0.
|
||||
|
||||
You can only use this parameter if the other two
|
||||
are not set.
|
||||
|
||||
Returns
|
||||
The :tl:`Updates` object that the request produces.
|
||||
|
||||
Example
|
||||
.. code-block:: python
|
||||
|
||||
# Archiving the first 5 dialogs
|
||||
dialogs = await client.get_dialogs(5)
|
||||
await client.edit_folder(dialogs, 1)
|
||||
|
||||
# Un-archiving the third dialog (archiving to folder 0)
|
||||
await client.edit_folder(dialog[2], 0)
|
||||
|
||||
# Moving the first dialog to folder 0 and the second to 1
|
||||
dialogs = await client.get_dialogs(2)
|
||||
await client.edit_folder(dialogs, [0, 1])
|
||||
|
||||
# Un-archiving all dialogs
|
||||
await client.edit_folder(unpack=1)
|
||||
"""
|
||||
if (entity is None) == (unpack is None):
|
||||
raise ValueError('You can only set either entities or unpack, not both')
|
||||
|
||||
|
@ -404,43 +225,6 @@ class DialogMethods:
|
|||
*,
|
||||
revoke: bool = False
|
||||
):
|
||||
"""
|
||||
Deletes a dialog (leaves a chat or channel).
|
||||
|
||||
This method can be used as a user and as a bot. However,
|
||||
bots will only be able to use it to leave groups and channels
|
||||
(trying to delete a private conversation will do nothing).
|
||||
|
||||
See also `Dialog.delete() <telethon.tl.custom.dialog.Dialog.delete>`.
|
||||
|
||||
Arguments
|
||||
entity (entities):
|
||||
The entity of the dialog to delete. If it's a chat or
|
||||
channel, you will leave it. Note that the chat itself
|
||||
is not deleted, only the dialog, because you left it.
|
||||
|
||||
revoke (`bool`, optional):
|
||||
On private chats, you may revoke the messages from
|
||||
the other peer too. By default, it's `False`. Set
|
||||
it to `True` to delete the history for both.
|
||||
|
||||
This makes no difference for bot accounts, who can
|
||||
only leave groups and channels.
|
||||
|
||||
Returns
|
||||
The :tl:`Updates` object that the request produces,
|
||||
or nothing for private conversations.
|
||||
|
||||
Example
|
||||
.. code-block:: python
|
||||
|
||||
# Deleting the first dialog
|
||||
dialogs = await client.get_dialogs(5)
|
||||
await client.delete_dialog(dialogs[0])
|
||||
|
||||
# Leaving a channel by username
|
||||
await client.delete_dialog('username')
|
||||
"""
|
||||
# If we have enough information (`Dialog.delete` gives it to us),
|
||||
# then we know we don't have to kick ourselves in deactivated chats.
|
||||
if isinstance(entity, types.Chat):
|
||||
|
@ -478,120 +262,6 @@ class DialogMethods:
|
|||
max_messages: int = 100,
|
||||
exclusive: bool = True,
|
||||
replies_are_responses: bool = True) -> custom.Conversation:
|
||||
"""
|
||||
Creates a `Conversation <telethon.tl.custom.conversation.Conversation>`
|
||||
with the given entity.
|
||||
|
||||
.. note::
|
||||
|
||||
This Conversation API has certain shortcomings, such as lacking
|
||||
persistence, poor interaction with other event handlers, and
|
||||
overcomplicated usage for anything beyond the simplest case.
|
||||
|
||||
If you plan to interact with a bot without handlers, this works
|
||||
fine, but when running a bot yourself, you may instead prefer
|
||||
to follow the advice from https://stackoverflow.com/a/62246569/.
|
||||
|
||||
This is not the same as just sending a message to create a "dialog"
|
||||
with them, but rather a way to easily send messages and await for
|
||||
responses or other reactions. Refer to its documentation for more.
|
||||
|
||||
Arguments
|
||||
entity (`entity`):
|
||||
The entity with which a new conversation should be opened.
|
||||
|
||||
timeout (`int` | `float`, optional):
|
||||
The default timeout (in seconds) *per action* to be used. You
|
||||
may also override this timeout on a per-method basis. By
|
||||
default each action can take up to 60 seconds (the value of
|
||||
this timeout).
|
||||
|
||||
total_timeout (`int` | `float`, optional):
|
||||
The total timeout (in seconds) to use for the whole
|
||||
conversation. This takes priority over per-action
|
||||
timeouts. After these many seconds pass, subsequent
|
||||
actions will result in ``asyncio.TimeoutError``.
|
||||
|
||||
max_messages (`int`, optional):
|
||||
The maximum amount of messages this conversation will
|
||||
remember. After these many messages arrive in the
|
||||
specified chat, subsequent actions will result in
|
||||
``ValueError``.
|
||||
|
||||
exclusive (`bool`, optional):
|
||||
By default, conversations are exclusive within a single
|
||||
chat. That means that while a conversation is open in a
|
||||
chat, you can't open another one in the same chat, unless
|
||||
you disable this flag.
|
||||
|
||||
If you try opening an exclusive conversation for
|
||||
a chat where it's already open, it will raise
|
||||
``AlreadyInConversationError``.
|
||||
|
||||
replies_are_responses (`bool`, optional):
|
||||
Whether replies should be treated as responses or not.
|
||||
|
||||
If the setting is enabled, calls to `conv.get_response
|
||||
<telethon.tl.custom.conversation.Conversation.get_response>`
|
||||
and a subsequent call to `conv.get_reply
|
||||
<telethon.tl.custom.conversation.Conversation.get_reply>`
|
||||
will return different messages, otherwise they may return
|
||||
the same message.
|
||||
|
||||
Consider the following scenario with one outgoing message,
|
||||
1, and two incoming messages, the second one replying::
|
||||
|
||||
Hello! <1
|
||||
2> (reply to 1) Hi!
|
||||
3> (reply to 1) How are you?
|
||||
|
||||
And the following code:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
async with client.conversation(chat) as conv:
|
||||
msg1 = await conv.send_message('Hello!')
|
||||
msg2 = await conv.get_response()
|
||||
msg3 = await conv.get_reply()
|
||||
|
||||
With the setting enabled, ``msg2`` will be ``'Hi!'`` and
|
||||
``msg3`` be ``'How are you?'`` since replies are also
|
||||
responses, and a response was already returned.
|
||||
|
||||
With the setting disabled, both ``msg2`` and ``msg3`` will
|
||||
be ``'Hi!'`` since one is a response and also a reply.
|
||||
|
||||
Returns
|
||||
A `Conversation <telethon.tl.custom.conversation.Conversation>`.
|
||||
|
||||
Example
|
||||
.. code-block:: python
|
||||
|
||||
# <you> denotes outgoing messages you sent
|
||||
# <usr> denotes incoming response messages
|
||||
with bot.conversation(chat) as conv:
|
||||
# <you> Hi!
|
||||
conv.send_message('Hi!')
|
||||
|
||||
# <usr> Hello!
|
||||
hello = conv.get_response()
|
||||
|
||||
# <you> Please tell me your name
|
||||
conv.send_message('Please tell me your name')
|
||||
|
||||
# <usr> ?
|
||||
name = conv.get_response().raw_text
|
||||
|
||||
while not any(x.isalpha() for x in name):
|
||||
# <you> Your name didn't have any letters! Try again
|
||||
conv.send_message("Your name didn't have any letters! Try again")
|
||||
|
||||
# <usr> Human
|
||||
name = conv.get_response().raw_text
|
||||
|
||||
# <you> Thanks Human!
|
||||
conv.send_message('Thanks {}!'.format(name))
|
||||
"""
|
||||
return custom.Conversation(
|
||||
self,
|
||||
entity,
|
||||
|
@ -602,5 +272,3 @@ class DialogMethods:
|
|||
replies_are_responses=replies_are_responses
|
||||
|
||||
)
|
||||
|
||||
# endregion
|
||||
|
|
|
@ -192,10 +192,6 @@ class _GenericDownloadIter(_DirectDownloadIter):
|
|||
self.request.offset -= self._stride
|
||||
|
||||
|
||||
class DownloadMethods:
|
||||
|
||||
# region Public methods
|
||||
|
||||
async def download_profile_photo(
|
||||
self: 'TelegramClient',
|
||||
entity: 'hints.EntityLike',
|
||||
|
@ -728,11 +724,7 @@ class DownloadMethods:
|
|||
msg_data=msg_data,
|
||||
)
|
||||
|
||||
# endregion
|
||||
|
||||
# region Private methods
|
||||
|
||||
@staticmethod
|
||||
def _get_thumb(thumbs, thumb):
|
||||
# Seems Telegram has changed the order and put `PhotoStrippedSize`
|
||||
# last while this is the smallest (layer 116). Ensure we have the
|
||||
|
@ -834,7 +826,6 @@ class DownloadMethods:
|
|||
)
|
||||
return result if file is bytes else file
|
||||
|
||||
@staticmethod
|
||||
def _get_kind_and_names(attributes):
|
||||
"""Gets kind and possible names for :tl:`DocumentAttribute`."""
|
||||
kind = 'document'
|
||||
|
@ -894,7 +885,6 @@ class DownloadMethods:
|
|||
|
||||
return result if file is bytes else file
|
||||
|
||||
@classmethod
|
||||
def _download_contact(cls, mm_contact, file):
|
||||
"""
|
||||
Specialized version of .download_media() for contacts.
|
||||
|
@ -936,7 +926,6 @@ class DownloadMethods:
|
|||
|
||||
return file
|
||||
|
||||
@classmethod
|
||||
async def _download_web_document(cls, web, file, progress_callback):
|
||||
"""
|
||||
Specialized version of .download_media() for web documents.
|
||||
|
@ -977,7 +966,6 @@ class DownloadMethods:
|
|||
|
||||
return f.getvalue() if in_memory else file
|
||||
|
||||
@staticmethod
|
||||
def _get_proper_filename(file, kind, extension,
|
||||
date=None, possible_names=None):
|
||||
"""Gets a proper filename for 'file', if this is a path.
|
||||
|
@ -1039,5 +1027,3 @@ class DownloadMethods:
|
|||
if not os.path.isfile(result):
|
||||
return result
|
||||
i += 1
|
||||
|
||||
# endregion
|
||||
|
|
|
@ -9,12 +9,7 @@ if typing.TYPE_CHECKING:
|
|||
from .telegramclient import TelegramClient
|
||||
|
||||
|
||||
class MessageParseMethods:
|
||||
|
||||
# region Public properties
|
||||
|
||||
@property
|
||||
def parse_mode(self: 'TelegramClient'):
|
||||
def get_parse_mode(self: 'TelegramClient'):
|
||||
"""
|
||||
This property is the default parse mode used when sending messages.
|
||||
Defaults to `telethon.extensions.markdown`. It will always
|
||||
|
@ -49,8 +44,7 @@ class MessageParseMethods:
|
|||
"""
|
||||
return self._parse_mode
|
||||
|
||||
@parse_mode.setter
|
||||
def parse_mode(self: 'TelegramClient', mode: str):
|
||||
def set_parse_mode(self: 'TelegramClient', mode: str):
|
||||
self._parse_mode = utils.sanitize_parse_mode(mode)
|
||||
|
||||
# endregion
|
||||
|
@ -224,5 +218,3 @@ class MessageParseMethods:
|
|||
else None
|
||||
for rnd in random_id
|
||||
]
|
||||
|
||||
# endregion
|
||||
|
|
|
@ -320,12 +320,6 @@ class _IDsIter(RequestIter):
|
|||
self.buffer.append(message)
|
||||
|
||||
|
||||
class MessageMethods:
|
||||
|
||||
# region Public methods
|
||||
|
||||
# region Message retrieval
|
||||
|
||||
def iter_messages(
|
||||
self: 'TelegramClient',
|
||||
entity: 'hints.EntityLike',
|
||||
|
@ -345,166 +339,6 @@ class MessageMethods:
|
|||
reply_to: int = None,
|
||||
scheduled: bool = False
|
||||
) -> 'typing.Union[_MessagesIter, _IDsIter]':
|
||||
"""
|
||||
Iterator over the messages for the given chat.
|
||||
|
||||
The default order is from newest to oldest, but this
|
||||
behaviour can be changed with the `reverse` parameter.
|
||||
|
||||
If either `search`, `filter` or `from_user` are provided,
|
||||
:tl:`messages.Search` will be used instead of :tl:`messages.getHistory`.
|
||||
|
||||
.. note::
|
||||
|
||||
Telegram's flood wait limit for :tl:`GetHistoryRequest` seems to
|
||||
be around 30 seconds per 10 requests, therefore a sleep of 1
|
||||
second is the default for this limit (or above).
|
||||
|
||||
Arguments
|
||||
entity (`entity`):
|
||||
The entity from whom to retrieve the message history.
|
||||
|
||||
It may be `None` to perform a global search, or
|
||||
to get messages by their ID from no particular chat.
|
||||
Note that some of the offsets will not work if this
|
||||
is the case.
|
||||
|
||||
Note that if you want to perform a global search,
|
||||
you **must** set a non-empty `search` string, a `filter`.
|
||||
or `from_user`.
|
||||
|
||||
limit (`int` | `None`, optional):
|
||||
Number of messages to be retrieved. Due to limitations with
|
||||
the API retrieving more than 3000 messages will take longer
|
||||
than half a minute (or even more based on previous calls).
|
||||
|
||||
The limit may also be `None`, which would eventually return
|
||||
the whole history.
|
||||
|
||||
offset_date (`datetime`):
|
||||
Offset date (messages *previous* to this date will be
|
||||
retrieved). Exclusive.
|
||||
|
||||
offset_id (`int`):
|
||||
Offset message ID (only messages *previous* to the given
|
||||
ID will be retrieved). Exclusive.
|
||||
|
||||
max_id (`int`):
|
||||
All the messages with a higher (newer) ID or equal to this will
|
||||
be excluded.
|
||||
|
||||
min_id (`int`):
|
||||
All the messages with a lower (older) ID or equal to this will
|
||||
be excluded.
|
||||
|
||||
add_offset (`int`):
|
||||
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 entity will be returned.
|
||||
|
||||
wait_time (`int`):
|
||||
Wait time (in seconds) between different
|
||||
:tl:`GetHistoryRequest`. Use this parameter to avoid hitting
|
||||
the ``FloodWaitError`` as needed. If left to `None`, it will
|
||||
default to 1 second only if the limit is higher than 3000.
|
||||
|
||||
If the ``ids`` parameter is used, this time will default
|
||||
to 10 seconds only if the amount of IDs is higher than 300.
|
||||
|
||||
ids (`int`, `list`):
|
||||
A single integer ID (or several IDs) for the message that
|
||||
should be returned. This parameter takes precedence over
|
||||
the rest (which will be ignored if this is set). This can
|
||||
for instance be used to get the message with ID 123 from
|
||||
a channel. Note that if the message doesn't exist, `None`
|
||||
will appear in its place, so that zipping the list of IDs
|
||||
with the messages can match one-to-one.
|
||||
|
||||
.. note::
|
||||
|
||||
At the time of writing, Telegram will **not** return
|
||||
:tl:`MessageEmpty` for :tl:`InputMessageReplyTo` IDs that
|
||||
failed (i.e. the message is not replying to any, or is
|
||||
replying to a deleted message). This means that it is
|
||||
**not** possible to match messages one-by-one, so be
|
||||
careful if you use non-integers in this parameter.
|
||||
|
||||
reverse (`bool`, optional):
|
||||
If set to `True`, the messages will be returned in reverse
|
||||
order (from oldest to newest, instead of the default newest
|
||||
to oldest). This also means that the meaning of `offset_id`
|
||||
and `offset_date` parameters is reversed, although they will
|
||||
still be exclusive. `min_id` becomes equivalent to `offset_id`
|
||||
instead of being `max_id` as well since messages are returned
|
||||
in ascending order.
|
||||
|
||||
You cannot use this if both `entity` and `ids` are `None`.
|
||||
|
||||
reply_to (`int`, optional):
|
||||
If set to a message ID, the messages that reply to this ID
|
||||
will be returned. This feature is also known as comments in
|
||||
posts of broadcast channels, or viewing threads in groups.
|
||||
|
||||
This feature can only be used in broadcast channels and their
|
||||
linked megagroups. Using it in a chat or private conversation
|
||||
will result in ``telethon.errors.PeerIdInvalidError`` to occur.
|
||||
|
||||
When using this parameter, the ``filter`` and ``search``
|
||||
parameters have no effect, since Telegram's API doesn't
|
||||
support searching messages in replies.
|
||||
|
||||
.. note::
|
||||
|
||||
This feature is used to get replies to a message in the
|
||||
*discussion* group. If the same broadcast channel sends
|
||||
a message and replies to it itself, that reply will not
|
||||
be included in the results.
|
||||
|
||||
scheduled (`bool`, optional):
|
||||
If set to `True`, messages which are scheduled will be returned.
|
||||
All other parameter will be ignored for this, except `entity`.
|
||||
|
||||
Yields
|
||||
Instances of `Message <telethon.tl.custom.message.Message>`.
|
||||
|
||||
Example
|
||||
.. code-block:: python
|
||||
|
||||
# From most-recent to oldest
|
||||
async for message in client.iter_messages(chat):
|
||||
print(message.id, message.text)
|
||||
|
||||
# From oldest to most-recent
|
||||
async for message in client.iter_messages(chat, reverse=True):
|
||||
print(message.id, message.text)
|
||||
|
||||
# Filter by sender
|
||||
async for message in client.iter_messages(chat, from_user='me'):
|
||||
print(message.text)
|
||||
|
||||
# Server-side search with fuzzy text
|
||||
async for message in client.iter_messages(chat, search='hello'):
|
||||
print(message.id)
|
||||
|
||||
# Filter by message type:
|
||||
from telethon.tl.types import InputMessagesFilterPhotos
|
||||
async for message in client.iter_messages(chat, filter=InputMessagesFilterPhotos):
|
||||
print(message.photo)
|
||||
|
||||
# Getting comments from a post in a channel:
|
||||
async for message in client.iter_messages(channel, reply_to=123):
|
||||
print(message.chat.title, message.text)
|
||||
"""
|
||||
if ids is not None:
|
||||
if not utils.is_list_like(ids):
|
||||
ids = [ids]
|
||||
|
@ -537,36 +371,6 @@ class MessageMethods:
|
|||
)
|
||||
|
||||
async def get_messages(self: 'TelegramClient', *args, **kwargs) -> 'hints.TotalList':
|
||||
"""
|
||||
Same as `iter_messages()`, but returns a
|
||||
`TotalList <telethon.helpers.TotalList>` instead.
|
||||
|
||||
If the `limit` is not set, it will be 1 by default unless both
|
||||
`min_id` **and** `max_id` are set (as *named* arguments), in
|
||||
which case the entire range will be returned.
|
||||
|
||||
This is so because any integer limit would be rather arbitrary and
|
||||
it's common to only want to fetch one message, but if a range is
|
||||
specified it makes sense that it should return the entirety of it.
|
||||
|
||||
If `ids` is present in the *named* arguments and is not a list,
|
||||
a single `Message <telethon.tl.custom.message.Message>` will be
|
||||
returned for convenience instead of a list.
|
||||
|
||||
Example
|
||||
.. code-block:: python
|
||||
|
||||
# Get 0 photos and print the total to show how many photos there are
|
||||
from telethon.tl.types import InputMessagesFilterPhotos
|
||||
photos = await client.get_messages(chat, 0, filter=InputMessagesFilterPhotos)
|
||||
print(photos.total)
|
||||
|
||||
# Get all the photos
|
||||
photos = await client.get_messages(chat, None, filter=InputMessagesFilterPhotos)
|
||||
|
||||
# Get messages by ID:
|
||||
message_1337 = await client.get_messages(chat, ids=1337)
|
||||
"""
|
||||
if len(args) == 1 and 'limit' not in kwargs:
|
||||
if 'min_id' in kwargs and 'max_id' in kwargs:
|
||||
kwargs['limit'] = None
|
||||
|
@ -585,11 +389,6 @@ class MessageMethods:
|
|||
|
||||
return await it.collect()
|
||||
|
||||
get_messages.__signature__ = inspect.signature(iter_messages)
|
||||
|
||||
# endregion
|
||||
|
||||
# region Message sending/editing/deleting
|
||||
|
||||
async def _get_comment_data(
|
||||
self: 'TelegramClient',
|
||||
|
@ -625,177 +424,6 @@ class MessageMethods:
|
|||
schedule: 'hints.DateLike' = None,
|
||||
comment_to: 'typing.Union[int, types.Message]' = None
|
||||
) -> 'types.Message':
|
||||
"""
|
||||
Sends a message to the specified user, chat or channel.
|
||||
|
||||
The default parse mode is the same as the official applications
|
||||
(a custom flavour of markdown). ``**bold**, `code` or __italic__``
|
||||
are available. In addition you can send ``[links](https://example.com)``
|
||||
and ``[mentions](@username)`` (or using IDs like in the Bot API:
|
||||
``[mention](tg://user?id=123456789)``) and ``pre`` blocks with three
|
||||
backticks.
|
||||
|
||||
Sending a ``/start`` command with a parameter (like ``?start=data``)
|
||||
is also done through this method. Simply send ``'/start data'`` to
|
||||
the bot.
|
||||
|
||||
See also `Message.respond() <telethon.tl.custom.message.Message.respond>`
|
||||
and `Message.reply() <telethon.tl.custom.message.Message.reply>`.
|
||||
|
||||
Arguments
|
||||
entity (`entity`):
|
||||
To who will it be sent.
|
||||
|
||||
message (`str` | `Message <telethon.tl.custom.message.Message>`):
|
||||
The message to be sent, or another message object to resend.
|
||||
|
||||
The maximum length for a message is 35,000 bytes or 4,096
|
||||
characters. Longer messages will not be sliced automatically,
|
||||
and you should slice them manually if the text to send is
|
||||
longer than said length.
|
||||
|
||||
reply_to (`int` | `Message <telethon.tl.custom.message.Message>`, optional):
|
||||
Whether to reply to a message or not. If an integer is provided,
|
||||
it should be the ID of the message that it should reply to.
|
||||
|
||||
attributes (`list`, optional):
|
||||
Optional attributes that override the inferred ones, like
|
||||
:tl:`DocumentAttributeFilename` and so on.
|
||||
|
||||
parse_mode (`object`, optional):
|
||||
See the `TelegramClient.parse_mode
|
||||
<telethon.client.messageparse.MessageParseMethods.parse_mode>`
|
||||
property for allowed values. Markdown parsing will be used by
|
||||
default.
|
||||
|
||||
formatting_entities (`list`, optional):
|
||||
A list of message formatting entities. When provided, the ``parse_mode`` is ignored.
|
||||
|
||||
link_preview (`bool`, optional):
|
||||
Should the link preview be shown?
|
||||
|
||||
file (`file`, optional):
|
||||
Sends a message with a file attached (e.g. a photo,
|
||||
video, audio or document). The ``message`` may be empty.
|
||||
|
||||
thumb (`str` | `bytes` | `file`, optional):
|
||||
Optional JPEG thumbnail (for documents). **Telegram will
|
||||
ignore this parameter** unless you pass a ``.jpg`` file!
|
||||
The file must also be small in dimensions and in disk size.
|
||||
Successful thumbnails were files below 20kB and 320x320px.
|
||||
Width/height and dimensions/size ratios may be important.
|
||||
For Telegram to accept a thumbnail, you must provide the
|
||||
dimensions of the underlying media through ``attributes=``
|
||||
with :tl:`DocumentAttributesVideo` or by installing the
|
||||
optional ``hachoir`` dependency.
|
||||
|
||||
force_document (`bool`, optional):
|
||||
Whether to send the given file as a document or not.
|
||||
|
||||
clear_draft (`bool`, optional):
|
||||
Whether the existing draft should be cleared or not.
|
||||
|
||||
buttons (`list`, `custom.Button <telethon.tl.custom.button.Button>`, :tl:`KeyboardButton`):
|
||||
The matrix (list of lists), row list or button to be shown
|
||||
after sending the message. This parameter will only work if
|
||||
you have signed in as a bot. You can also pass your own
|
||||
:tl:`ReplyMarkup` here.
|
||||
|
||||
All the following limits apply together:
|
||||
|
||||
* There can be 100 buttons at most (any more are ignored).
|
||||
* There can be 8 buttons per row at most (more are ignored).
|
||||
* The maximum callback data per button is 64 bytes.
|
||||
* The maximum data that can be embedded in total is just
|
||||
over 4KB, shared between inline callback data and text.
|
||||
|
||||
silent (`bool`, optional):
|
||||
Whether the message should notify people in a broadcast
|
||||
channel or not. Defaults to `False`, which means it will
|
||||
notify them. Set it to `True` to alter this behaviour.
|
||||
|
||||
background (`bool`, optional):
|
||||
Whether the message should be send in background.
|
||||
|
||||
supports_streaming (`bool`, optional):
|
||||
Whether the sent video supports streaming or not. Note that
|
||||
Telegram only recognizes as streamable some formats like MP4,
|
||||
and others like AVI or MKV will not work. You should convert
|
||||
these to MP4 before sending if you want them to be streamable.
|
||||
Unsupported formats will result in ``VideoContentTypeError``.
|
||||
|
||||
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.
|
||||
|
||||
comment_to (`int` | `Message <telethon.tl.custom.message.Message>`, optional):
|
||||
Similar to ``reply_to``, but replies in the linked group of a
|
||||
broadcast channel instead (effectively leaving a "comment to"
|
||||
the specified message).
|
||||
|
||||
This parameter takes precedence over ``reply_to``. If there is
|
||||
no linked chat, `telethon.errors.sgIdInvalidError` is raised.
|
||||
|
||||
Returns
|
||||
The sent `custom.Message <telethon.tl.custom.message.Message>`.
|
||||
|
||||
Example
|
||||
.. code-block:: python
|
||||
|
||||
# Markdown is the default
|
||||
await client.send_message('me', 'Hello **world**!')
|
||||
|
||||
# Default to another parse mode
|
||||
client.parse_mode = 'html'
|
||||
|
||||
await client.send_message('me', 'Some <b>bold</b> and <i>italic</i> text')
|
||||
await client.send_message('me', 'An <a href="https://example.com">URL</a>')
|
||||
# code and pre tags also work, but those break the documentation :)
|
||||
await client.send_message('me', '<a href="tg://user?id=me">Mentions</a>')
|
||||
|
||||
# Explicit parse mode
|
||||
# No parse mode by default
|
||||
client.parse_mode = None
|
||||
|
||||
# ...but here I want markdown
|
||||
await client.send_message('me', 'Hello, **world**!', parse_mode='md')
|
||||
|
||||
# ...and here I need HTML
|
||||
await client.send_message('me', 'Hello, <i>world</i>!', parse_mode='html')
|
||||
|
||||
# If you logged in as a bot account, you can send buttons
|
||||
from telethon import events, Button
|
||||
|
||||
@client.on(events.CallbackQuery)
|
||||
async def callback(event):
|
||||
await event.edit('Thank you for clicking {}!'.format(event.data))
|
||||
|
||||
# Single inline button
|
||||
await client.send_message(chat, 'A single button, with "clk1" as data',
|
||||
buttons=Button.inline('Click me', b'clk1'))
|
||||
|
||||
# Matrix of inline buttons
|
||||
await client.send_message(chat, 'Pick one from this grid', buttons=[
|
||||
[Button.inline('Left'), Button.inline('Right')],
|
||||
[Button.url('Check this site!', 'https://example.com')]
|
||||
])
|
||||
|
||||
# Reply keyboard
|
||||
await client.send_message(chat, 'Welcome', buttons=[
|
||||
Button.text('Thanks!', resize=True, single_use=True),
|
||||
Button.request_phone('Send phone'),
|
||||
Button.request_location('Send location')
|
||||
])
|
||||
|
||||
# Forcing replies or clearing buttons.
|
||||
await client.send_message(chat, 'Reply to me', buttons=Button.force_reply())
|
||||
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:
|
||||
return await self.send_file(
|
||||
entity, file, caption=message, reply_to=reply_to,
|
||||
|
@ -899,74 +527,6 @@ class MessageMethods:
|
|||
as_album: bool = None,
|
||||
schedule: 'hints.DateLike' = None
|
||||
) -> 'typing.Sequence[types.Message]':
|
||||
"""
|
||||
Forwards the given messages to the specified entity.
|
||||
|
||||
If you want to "forward" a message without the forward header
|
||||
(the "forwarded from" text), you should use `send_message` with
|
||||
the original message instead. This will send a copy of it.
|
||||
|
||||
See also `Message.forward_to() <telethon.tl.custom.message.Message.forward_to>`.
|
||||
|
||||
Arguments
|
||||
entity (`entity`):
|
||||
To which entity the message(s) will be forwarded.
|
||||
|
||||
messages (`list` | `int` | `Message <telethon.tl.custom.message.Message>`):
|
||||
The message(s) to forward, or their integer IDs.
|
||||
|
||||
from_peer (`entity`):
|
||||
If the given messages are integer IDs and not instances
|
||||
of the ``Message`` class, this *must* be specified in
|
||||
order for the forward to work. This parameter indicates
|
||||
the entity from which the messages should be forwarded.
|
||||
|
||||
silent (`bool`, optional):
|
||||
Whether the message should notify people with sound or not.
|
||||
Defaults to `False` (send with a notification sound unless
|
||||
the person has the chat muted). Set it to `True` to alter
|
||||
this behaviour.
|
||||
|
||||
background (`bool`, optional):
|
||||
Whether the message should be forwarded in background.
|
||||
|
||||
with_my_score (`bool`, optional):
|
||||
Whether forwarded should contain your game score.
|
||||
|
||||
as_album (`bool`, optional):
|
||||
This flag no longer has any effect.
|
||||
|
||||
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
|
||||
The list of forwarded `Message <telethon.tl.custom.message.Message>`,
|
||||
or a single one if a list wasn't provided as input.
|
||||
|
||||
Note that if all messages are invalid (i.e. deleted) the call
|
||||
will fail with ``MessageIdInvalidError``. If only some are
|
||||
invalid, the list will have `None` instead of those messages.
|
||||
|
||||
Example
|
||||
.. code-block:: python
|
||||
|
||||
# a single one
|
||||
await client.forward_messages(chat, message)
|
||||
# or
|
||||
await client.forward_messages(chat, message_id, from_chat)
|
||||
# or
|
||||
await message.forward_to(chat)
|
||||
|
||||
# multiple
|
||||
await client.forward_messages(chat, messages)
|
||||
# or
|
||||
await client.forward_messages(chat, message_ids, from_chat)
|
||||
|
||||
# Forwarding as a copy
|
||||
await client.send_message(chat, message)
|
||||
"""
|
||||
if as_album is not None:
|
||||
warnings.warn('the as_album argument is deprecated and no longer has any effect')
|
||||
|
||||
|
@ -1033,116 +593,6 @@ class MessageMethods:
|
|||
supports_streaming: bool = False,
|
||||
schedule: 'hints.DateLike' = None
|
||||
) -> 'types.Message':
|
||||
"""
|
||||
Edits the given message to change its text or media.
|
||||
|
||||
See also `Message.edit() <telethon.tl.custom.message.Message.edit>`.
|
||||
|
||||
Arguments
|
||||
entity (`entity` | `Message <telethon.tl.custom.message.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.
|
||||
|
||||
You may also pass a :tl:`InputBotInlineMessageID`,
|
||||
which is the only way to edit messages that were sent
|
||||
after the user selects an inline query result.
|
||||
|
||||
message (`int` | `Message <telethon.tl.custom.message.Message>` | `str`):
|
||||
The ID of the message (or `Message
|
||||
<telethon.tl.custom.message.Message>` itself) to be edited.
|
||||
If the `entity` was a `Message
|
||||
<telethon.tl.custom.message.Message>`, then this message
|
||||
will be treated as the new text.
|
||||
|
||||
text (`str`, optional):
|
||||
The new text of the message. Does nothing if the `entity`
|
||||
was a `Message <telethon.tl.custom.message.Message>`.
|
||||
|
||||
parse_mode (`object`, optional):
|
||||
See the `TelegramClient.parse_mode
|
||||
<telethon.client.messageparse.MessageParseMethods.parse_mode>`
|
||||
property for allowed values. Markdown parsing will be used by
|
||||
default.
|
||||
|
||||
attributes (`list`, optional):
|
||||
Optional attributes that override the inferred ones, like
|
||||
:tl:`DocumentAttributeFilename` and so on.
|
||||
|
||||
formatting_entities (`list`, optional):
|
||||
A list of message formatting entities. When provided, the ``parse_mode`` is ignored.
|
||||
|
||||
link_preview (`bool`, optional):
|
||||
Should the link preview be shown?
|
||||
|
||||
file (`str` | `bytes` | `file` | `media`, optional):
|
||||
The file object that should replace the existing media
|
||||
in the message.
|
||||
|
||||
thumb (`str` | `bytes` | `file`, optional):
|
||||
Optional JPEG thumbnail (for documents). **Telegram will
|
||||
ignore this parameter** unless you pass a ``.jpg`` file!
|
||||
The file must also be small in dimensions and in disk size.
|
||||
Successful thumbnails were files below 20kB and 320x320px.
|
||||
Width/height and dimensions/size ratios may be important.
|
||||
For Telegram to accept a thumbnail, you must provide the
|
||||
dimensions of the underlying media through ``attributes=``
|
||||
with :tl:`DocumentAttributesVideo` or by installing the
|
||||
optional ``hachoir`` dependency.
|
||||
|
||||
force_document (`bool`, optional):
|
||||
Whether to send the given file as a document or not.
|
||||
|
||||
buttons (`list`, `custom.Button <telethon.tl.custom.button.Button>`, :tl:`KeyboardButton`):
|
||||
The matrix (list of lists), row list or button to be shown
|
||||
after sending the message. This parameter will only work if
|
||||
you have signed in as a bot. You can also pass your own
|
||||
:tl:`ReplyMarkup` here.
|
||||
|
||||
supports_streaming (`bool`, optional):
|
||||
Whether the sent video supports streaming or not. Note that
|
||||
Telegram only recognizes as streamable some formats like MP4,
|
||||
and others like AVI or MKV will not work. You should convert
|
||||
these to MP4 before sending if you want them to be streamable.
|
||||
Unsupported formats will result in ``VideoContentTypeError``.
|
||||
|
||||
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
|
||||
The edited `Message <telethon.tl.custom.message.Message>`,
|
||||
unless `entity` was a :tl:`InputBotInlineMessageID` in which
|
||||
case this method returns a boolean.
|
||||
|
||||
Raises
|
||||
``MessageAuthorRequiredError`` if you're not the author of the
|
||||
message but tried editing it anyway.
|
||||
|
||||
``MessageNotModifiedError`` if the contents of the message were
|
||||
not modified at all.
|
||||
|
||||
``MessageIdInvalidError`` if the ID of the message is invalid
|
||||
(the ID itself may be correct, but the message with that ID
|
||||
cannot be edited). For example, when trying to edit messages
|
||||
with a reply markup (or clear markup) this error will be raised.
|
||||
|
||||
Example
|
||||
.. code-block:: python
|
||||
|
||||
message = await client.send_message(chat, 'hello')
|
||||
|
||||
await client.edit_message(chat, message, 'hello!')
|
||||
# or
|
||||
await client.edit_message(chat, message.id, 'hello!!')
|
||||
# or
|
||||
await client.edit_message(message, 'hello!!!')
|
||||
"""
|
||||
if isinstance(entity, types.InputBotInlineMessageID):
|
||||
text = text or message
|
||||
message = entity
|
||||
|
@ -1200,50 +650,6 @@ class MessageMethods:
|
|||
message_ids: 'typing.Union[hints.MessageIDLike, typing.Sequence[hints.MessageIDLike]]',
|
||||
*,
|
||||
revoke: bool = True) -> 'typing.Sequence[types.messages.AffectedMessages]':
|
||||
"""
|
||||
Deletes the given messages, optionally "for everyone".
|
||||
|
||||
See also `Message.delete() <telethon.tl.custom.message.Message.delete>`.
|
||||
|
||||
.. warning::
|
||||
|
||||
This method does **not** validate that the message IDs belong
|
||||
to the chat that you passed! It's possible for the method to
|
||||
delete messages from different private chats and small group
|
||||
chats at once, so make sure to pass the right IDs.
|
||||
|
||||
Arguments
|
||||
entity (`entity`):
|
||||
From who the message will be deleted. This can actually
|
||||
be `None` for normal chats, but **must** be present
|
||||
for channels and megagroups.
|
||||
|
||||
message_ids (`list` | `int` | `Message <telethon.tl.custom.message.Message>`):
|
||||
The IDs (or ID) or messages to be deleted.
|
||||
|
||||
revoke (`bool`, optional):
|
||||
Whether the message should be deleted for everyone or not.
|
||||
By default it has the opposite behaviour of official clients,
|
||||
and it will delete the message for everyone.
|
||||
|
||||
`Since 24 March 2019
|
||||
<https://telegram.org/blog/unsend-privacy-emoji>`_, you can
|
||||
also revoke messages of any age (i.e. messages sent long in
|
||||
the past) the *other* person sent in private conversations
|
||||
(and of course your messages too).
|
||||
|
||||
Disabling this has no effect on channels or megagroups,
|
||||
since it will unconditionally delete the message for everyone.
|
||||
|
||||
Returns
|
||||
A list of :tl:`AffectedMessages`, each item being the result
|
||||
for the delete calls of the messages in chunks of 100 each.
|
||||
|
||||
Example
|
||||
.. code-block:: python
|
||||
|
||||
await client.delete_messages(chat, messages)
|
||||
"""
|
||||
if not utils.is_list_like(message_ids):
|
||||
message_ids = (message_ids,)
|
||||
|
||||
|
@ -1267,10 +673,6 @@ class MessageMethods:
|
|||
return await self([functions.messages.DeleteMessagesRequest(
|
||||
list(c), revoke) for c in utils.chunks(message_ids)])
|
||||
|
||||
# endregion
|
||||
|
||||
# region Miscellaneous
|
||||
|
||||
async def send_read_acknowledge(
|
||||
self: 'TelegramClient',
|
||||
entity: 'hints.EntityLike',
|
||||
|
@ -1278,49 +680,6 @@ class MessageMethods:
|
|||
*,
|
||||
max_id: int = None,
|
||||
clear_mentions: bool = False) -> bool:
|
||||
"""
|
||||
Marks messages as read and optionally clears mentions.
|
||||
|
||||
This effectively marks a message as read (or more than one) in the
|
||||
given conversation.
|
||||
|
||||
If neither message nor maximum ID are provided, all messages will be
|
||||
marked as read by assuming that ``max_id = 0``.
|
||||
|
||||
If a message or maximum ID is provided, all the messages up to and
|
||||
including such ID will be marked as read (for all messages whose ID
|
||||
≤ max_id).
|
||||
|
||||
See also `Message.mark_read() <telethon.tl.custom.message.Message.mark_read>`.
|
||||
|
||||
Arguments
|
||||
entity (`entity`):
|
||||
The chat where these messages are located.
|
||||
|
||||
message (`list` | `Message <telethon.tl.custom.message.Message>`):
|
||||
Either a list of messages or a single message.
|
||||
|
||||
max_id (`int`):
|
||||
Until which message should the read acknowledge be sent for.
|
||||
This has priority over the ``message`` parameter.
|
||||
|
||||
clear_mentions (`bool`):
|
||||
Whether the mention badge should be cleared (so that
|
||||
there are no more mentions) or not for the given entity.
|
||||
|
||||
If no message is provided, this will be the only action
|
||||
taken.
|
||||
|
||||
Example
|
||||
.. code-block:: python
|
||||
|
||||
# using a Message object
|
||||
await client.send_read_acknowledge(chat, message)
|
||||
# ...or using the int ID of a Message
|
||||
await client.send_read_acknowledge(chat, message_id)
|
||||
# ...or passing a list of messages to mark as read
|
||||
await client.send_read_acknowledge(chat, messages)
|
||||
"""
|
||||
if max_id is None:
|
||||
if not message:
|
||||
max_id = 0
|
||||
|
@ -1354,37 +713,6 @@ class MessageMethods:
|
|||
notify: bool = False,
|
||||
pm_oneside: bool = False
|
||||
):
|
||||
"""
|
||||
Pins a message in a chat.
|
||||
|
||||
The default behaviour is to *not* notify members, unlike the
|
||||
official applications.
|
||||
|
||||
See also `Message.pin() <telethon.tl.custom.message.Message.pin>`.
|
||||
|
||||
Arguments
|
||||
entity (`entity`):
|
||||
The chat where the message should be pinned.
|
||||
|
||||
message (`int` | `Message <telethon.tl.custom.message.Message>`):
|
||||
The message or the message ID to pin. If it's
|
||||
`None`, all messages will be unpinned instead.
|
||||
|
||||
notify (`bool`, optional):
|
||||
Whether the pin should notify people or not.
|
||||
|
||||
pm_oneside (`bool`, optional):
|
||||
Whether the message should be pinned for everyone or not.
|
||||
By default it has the opposite behaviour of official clients,
|
||||
and it will pin the message for both sides, in private chats.
|
||||
|
||||
Example
|
||||
.. code-block:: python
|
||||
|
||||
# Send and pin a message to annoy everyone
|
||||
message = await client.send_message(chat, 'Pinotifying is fun!')
|
||||
await client.pin_message(chat, message, notify=True)
|
||||
"""
|
||||
return await self._pin(entity, message, unpin=False, notify=notify, pm_oneside=pm_oneside)
|
||||
|
||||
async def unpin_message(
|
||||
|
@ -1394,27 +722,6 @@ class MessageMethods:
|
|||
*,
|
||||
notify: bool = False
|
||||
):
|
||||
"""
|
||||
Unpins a message in a chat.
|
||||
|
||||
If no message ID is specified, all pinned messages will be unpinned.
|
||||
|
||||
See also `Message.unpin() <telethon.tl.custom.message.Message.unpin>`.
|
||||
|
||||
Arguments
|
||||
entity (`entity`):
|
||||
The chat where the message should be pinned.
|
||||
|
||||
message (`int` | `Message <telethon.tl.custom.message.Message>`):
|
||||
The message or the message ID to unpin. If it's
|
||||
`None`, all messages will be unpinned instead.
|
||||
|
||||
Example
|
||||
.. code-block:: python
|
||||
|
||||
# Unpin all messages from a chat
|
||||
await client.unpin_message(chat)
|
||||
"""
|
||||
return await self._pin(entity, message, unpin=True, notify=notify)
|
||||
|
||||
async def _pin(self, entity, message, *, unpin, notify=False, pm_oneside=False):
|
||||
|
@ -1442,7 +749,3 @@ class MessageMethods:
|
|||
|
||||
# Pinning a message that doesn't exist would RPC-error earlier
|
||||
return self._get_response_message(request, result, entity)
|
||||
|
||||
# endregion
|
||||
|
||||
# endregion
|
||||
|
|
|
@ -64,162 +64,9 @@ class _ExportState:
|
|||
|
||||
|
||||
# TODO How hard would it be to support both `trio` and `asyncio`?
|
||||
class TelegramBaseClient(abc.ABC):
|
||||
"""
|
||||
This is the abstract base class for the client. It defines some
|
||||
basic stuff like connecting, switching data center, etc, and
|
||||
leaves the `__call__` unimplemented.
|
||||
|
||||
Arguments
|
||||
session (`str` | `telethon.sessions.abstract.Session`, `None`):
|
||||
The file name of the session file to be used if a string is
|
||||
given (it may be a full path), or the Session instance to be
|
||||
used otherwise. If it's `None`, the session will not be saved,
|
||||
and you should call :meth:`.log_out()` when you're done.
|
||||
|
||||
Note that if you pass a string it will be a file in the current
|
||||
working directory, although you can also pass absolute paths.
|
||||
|
||||
The session file contains enough information for you to login
|
||||
without re-sending the code, so if you have to enter the code
|
||||
more than once, maybe you're changing the working directory,
|
||||
renaming or removing the file, or using random names.
|
||||
|
||||
api_id (`int` | `str`):
|
||||
The API ID you obtained from https://my.telegram.org.
|
||||
|
||||
api_hash (`str`):
|
||||
The API hash you obtained from https://my.telegram.org.
|
||||
|
||||
connection (`telethon.network.connection.common.Connection`, optional):
|
||||
The connection instance to be used when creating a new connection
|
||||
to the servers. It **must** be a type.
|
||||
|
||||
Defaults to `telethon.network.connection.tcpfull.ConnectionTcpFull`.
|
||||
|
||||
use_ipv6 (`bool`, optional):
|
||||
Whether to connect to the servers through IPv6 or not.
|
||||
By default this is `False` as IPv6 support is not
|
||||
too widespread yet.
|
||||
|
||||
proxy (`tuple` | `list` | `dict`, optional):
|
||||
An iterable consisting of the proxy info. If `connection` is
|
||||
one of `MTProxy`, then it should contain MTProxy credentials:
|
||||
``('hostname', port, 'secret')``. Otherwise, it's meant to store
|
||||
function parameters for PySocks, like ``(type, 'hostname', port)``.
|
||||
See https://github.com/Anorov/PySocks#usage-1 for more.
|
||||
|
||||
local_addr (`str` | `tuple`, optional):
|
||||
Local host address (and port, optionally) used to bind the socket to locally.
|
||||
You only need to use this if you have multiple network cards and
|
||||
want to use a specific one.
|
||||
|
||||
timeout (`int` | `float`, optional):
|
||||
The timeout in seconds to be used when connecting.
|
||||
This is **not** the timeout to be used when ``await``'ing for
|
||||
invoked requests, and you should use ``asyncio.wait`` or
|
||||
``asyncio.wait_for`` for that.
|
||||
|
||||
request_retries (`int` | `None`, optional):
|
||||
How many times a request should be retried. Request are retried
|
||||
when Telegram is having internal issues (due to either
|
||||
``errors.ServerError`` or ``errors.RpcCallFailError``),
|
||||
when there is a ``errors.FloodWaitError`` less than
|
||||
`flood_sleep_threshold`, or when there's a migrate error.
|
||||
|
||||
May take a negative or `None` value for infinite retries, but
|
||||
this is not recommended, since some requests can always trigger
|
||||
a call fail (such as searching for messages).
|
||||
|
||||
connection_retries (`int` | `None`, optional):
|
||||
How many times the reconnection should retry, either on the
|
||||
initial connection or when Telegram disconnects us. May be
|
||||
set to a negative or `None` value for infinite retries, but
|
||||
this is not recommended, since the program can get stuck in an
|
||||
infinite loop.
|
||||
|
||||
retry_delay (`int` | `float`, optional):
|
||||
The delay in seconds to sleep between automatic reconnections.
|
||||
|
||||
auto_reconnect (`bool`, optional):
|
||||
Whether reconnection should be retried `connection_retries`
|
||||
times automatically if Telegram disconnects us or not.
|
||||
|
||||
sequential_updates (`bool`, optional):
|
||||
By default every incoming update will create a new task, so
|
||||
you can handle several updates in parallel. Some scripts need
|
||||
the order in which updates are processed to be sequential, and
|
||||
this setting allows them to do so.
|
||||
|
||||
If set to `True`, incoming updates will be put in a queue
|
||||
and processed sequentially. This means your event handlers
|
||||
should *not* perform long-running operations since new
|
||||
updates are put inside of an unbounded queue.
|
||||
|
||||
flood_sleep_threshold (`int` | `float`, optional):
|
||||
The threshold below which the library should automatically
|
||||
sleep on flood wait and slow mode wait errors (inclusive). For instance, if a
|
||||
``FloodWaitError`` for 17s occurs and `flood_sleep_threshold`
|
||||
is 20s, the library will ``sleep`` automatically. If the error
|
||||
was for 21s, it would ``raise FloodWaitError`` instead. Values
|
||||
larger than a day (like ``float('inf')``) will be changed to a day.
|
||||
|
||||
raise_last_call_error (`bool`, optional):
|
||||
When API calls fail in a way that causes Telethon to retry
|
||||
automatically, should the RPC error of the last attempt be raised
|
||||
instead of a generic ValueError. This is mostly useful for
|
||||
detecting when Telegram has internal issues.
|
||||
|
||||
device_model (`str`, optional):
|
||||
"Device model" to be sent when creating the initial connection.
|
||||
Defaults to 'PC (n)bit' derived from ``platform.uname().machine``, or its direct value if unknown.
|
||||
|
||||
system_version (`str`, optional):
|
||||
"System version" to be sent when creating the initial connection.
|
||||
Defaults to ``platform.uname().release`` stripped of everything ahead of -.
|
||||
|
||||
app_version (`str`, optional):
|
||||
"App version" to be sent when creating the initial connection.
|
||||
Defaults to `telethon.version.__version__`.
|
||||
|
||||
lang_code (`str`, optional):
|
||||
"Language code" to be sent when creating the initial connection.
|
||||
Defaults to ``'en'``.
|
||||
|
||||
system_lang_code (`str`, optional):
|
||||
"System lang code" to be sent when creating the initial connection.
|
||||
Defaults to `lang_code`.
|
||||
|
||||
loop (`asyncio.AbstractEventLoop`, optional):
|
||||
Asyncio event loop to use. Defaults to `asyncio.get_event_loop()`.
|
||||
This argument is ignored.
|
||||
|
||||
base_logger (`str` | `logging.Logger`, optional):
|
||||
Base logger name or instance to use.
|
||||
If a `str` is given, it'll be passed to `logging.getLogger()`. If a
|
||||
`logging.Logger` is given, it'll be used directly. If something
|
||||
else or nothing is given, the default logger will be used.
|
||||
|
||||
receive_updates (`bool`, optional):
|
||||
Whether the client will receive updates or not. By default, updates
|
||||
will be received from Telegram as they occur.
|
||||
|
||||
Turning this off means that Telegram will not send updates at all
|
||||
so event handlers, conversations, and QR login will not work.
|
||||
However, certain scripts don't need updates, so this will reduce
|
||||
the amount of bandwidth used.
|
||||
"""
|
||||
|
||||
# Current TelegramClient version
|
||||
__version__ = version.__version__
|
||||
|
||||
# Cached server configuration (with .dc_options), can be "global"
|
||||
_config = None
|
||||
_cdn_config = None
|
||||
|
||||
# region Initialization
|
||||
|
||||
def __init__(
|
||||
def init(
|
||||
self: 'TelegramClient',
|
||||
session: 'typing.Union[str, Session]',
|
||||
api_id: int,
|
||||
|
@ -448,80 +295,22 @@ class TelegramBaseClient(abc.ABC):
|
|||
# A place to store if channels are a megagroup or not (see `edit_admin`)
|
||||
self._megagroup_cache = {}
|
||||
|
||||
# endregion
|
||||
|
||||
# region Properties
|
||||
|
||||
@property
|
||||
def loop(self: 'TelegramClient') -> asyncio.AbstractEventLoop:
|
||||
"""
|
||||
Property with the ``asyncio`` event loop used by this client.
|
||||
|
||||
Example
|
||||
.. code-block:: python
|
||||
|
||||
# Download media in the background
|
||||
task = client.loop.create_task(message.download_media())
|
||||
|
||||
# Do some work
|
||||
...
|
||||
|
||||
# Join the task (wait for it to complete)
|
||||
await task
|
||||
"""
|
||||
def get_loop(self: 'TelegramClient') -> asyncio.AbstractEventLoop:
|
||||
return asyncio.get_event_loop()
|
||||
|
||||
@property
|
||||
def disconnected(self: 'TelegramClient') -> asyncio.Future:
|
||||
"""
|
||||
Property with a ``Future`` that resolves upon disconnection.
|
||||
|
||||
Example
|
||||
.. code-block:: python
|
||||
|
||||
# Wait for a disconnection to occur
|
||||
try:
|
||||
await client.disconnected
|
||||
except OSError:
|
||||
print('Error on disconnect')
|
||||
"""
|
||||
def get_disconnected(self: 'TelegramClient') -> asyncio.Future:
|
||||
return self._sender.disconnected
|
||||
|
||||
@property
|
||||
def flood_sleep_threshold(self):
|
||||
def get_flood_sleep_threshold(self):
|
||||
return self._flood_sleep_threshold
|
||||
|
||||
@flood_sleep_threshold.setter
|
||||
def flood_sleep_threshold(self, value):
|
||||
def set_flood_sleep_threshold(self, value):
|
||||
# None -> 0, negative values don't really matter
|
||||
self._flood_sleep_threshold = min(value or 0, 24 * 60 * 60)
|
||||
|
||||
# endregion
|
||||
|
||||
# region Connecting
|
||||
|
||||
async def connect(self: 'TelegramClient') -> None:
|
||||
"""
|
||||
Connects to Telegram.
|
||||
|
||||
.. note::
|
||||
|
||||
Connect means connect and nothing else, and only one low-level
|
||||
request is made to notify Telegram about which layer we will be
|
||||
using.
|
||||
|
||||
Before Telegram sends you updates, you need to make a high-level
|
||||
request, like `client.get_me() <telethon.client.users.UserMethods.get_me>`,
|
||||
as described in https://core.telegram.org/api/updates.
|
||||
|
||||
Example
|
||||
.. code-block:: python
|
||||
|
||||
try:
|
||||
await client.connect()
|
||||
except OSError:
|
||||
print('Failed to connect')
|
||||
"""
|
||||
if not await self._sender.connect(self._connection(
|
||||
self.session.server_address,
|
||||
self.session.port,
|
||||
|
@ -545,34 +334,10 @@ class TelegramBaseClient(abc.ABC):
|
|||
self._updates_handle = self.loop.create_task(self._update_loop())
|
||||
|
||||
def is_connected(self: 'TelegramClient') -> bool:
|
||||
"""
|
||||
Returns `True` if the user has connected.
|
||||
|
||||
This method is **not** asynchronous (don't use ``await`` on it).
|
||||
|
||||
Example
|
||||
.. code-block:: python
|
||||
|
||||
while client.is_connected():
|
||||
await asyncio.sleep(1)
|
||||
"""
|
||||
sender = getattr(self, '_sender', None)
|
||||
return sender and sender.is_connected()
|
||||
|
||||
def disconnect(self: 'TelegramClient'):
|
||||
"""
|
||||
Disconnects from Telegram.
|
||||
|
||||
If the event loop is already running, this method returns a
|
||||
coroutine that you should await on your own code; otherwise
|
||||
the loop is ran until said coroutine completes.
|
||||
|
||||
Example
|
||||
.. code-block:: python
|
||||
|
||||
# You don't need to use this if you used "with client"
|
||||
await client.disconnect()
|
||||
"""
|
||||
if self.loop.is_running():
|
||||
return self._disconnect_coro()
|
||||
else:
|
||||
|
@ -587,15 +352,6 @@ class TelegramBaseClient(abc.ABC):
|
|||
pass
|
||||
|
||||
def set_proxy(self: 'TelegramClient', proxy: typing.Union[tuple, dict]):
|
||||
"""
|
||||
Changes the proxy which will be used on next (re)connection.
|
||||
|
||||
Method has no immediate effects if the client is currently connected.
|
||||
|
||||
The new proxy will take it's effect on the next reconnection attempt:
|
||||
- on a call `await client.connect()` (after complete disconnect)
|
||||
- on auto-reconnect attempt (e.g, after previous connection was lost)
|
||||
"""
|
||||
init_proxy = None if not issubclass(self._connection, TcpMTProxy) else \
|
||||
types.InputClientProxy(*self._connection.address_info(proxy))
|
||||
|
||||
|
@ -689,9 +445,6 @@ class TelegramBaseClient(abc.ABC):
|
|||
self.session.auth_key = auth_key
|
||||
self.session.save()
|
||||
|
||||
# endregion
|
||||
|
||||
# region Working with different connections/Data Centers
|
||||
|
||||
async def _get_dc(self: 'TelegramClient', dc_id, cdn=False):
|
||||
"""Gets the Data Center (DC) associated to 'dc_id'"""
|
||||
|
@ -830,34 +583,9 @@ class TelegramBaseClient(abc.ABC):
|
|||
client.connect(_sync_updates=False)
|
||||
return client
|
||||
|
||||
# endregion
|
||||
|
||||
# region Invoking Telegram requests
|
||||
|
||||
@abc.abstractmethod
|
||||
def __call__(self: 'TelegramClient', request, ordered=False):
|
||||
"""
|
||||
Invokes (sends) one or more MTProtoRequests and returns (receives)
|
||||
their result.
|
||||
|
||||
Args:
|
||||
request (`TLObject` | `list`):
|
||||
The request or requests to be invoked.
|
||||
|
||||
ordered (`bool`, optional):
|
||||
Whether the requests (if more than one was given) should be
|
||||
executed sequentially on the server. They run in arbitrary
|
||||
order by default.
|
||||
|
||||
flood_sleep_threshold (`int` | `None`, optional):
|
||||
The flood sleep threshold to use for this request. This overrides
|
||||
the default value stored in
|
||||
`client.flood_sleep_threshold <telethon.client.telegrambaseclient.TelegramBaseClient.flood_sleep_threshold>`
|
||||
|
||||
Returns:
|
||||
The result of the request (often a `TLObject`) or a list of
|
||||
results if more than one request was given.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@abc.abstractmethod
|
||||
|
@ -871,5 +599,3 @@ class TelegramBaseClient(abc.ABC):
|
|||
@abc.abstractmethod
|
||||
async def _handle_auto_reconnect(self: 'TelegramClient'):
|
||||
raise NotImplementedError
|
||||
|
||||
# endregion
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -18,9 +18,6 @@ if typing.TYPE_CHECKING:
|
|||
|
||||
Callback = typing.Callable[[typing.Any], typing.Any]
|
||||
|
||||
class UpdateMethods:
|
||||
|
||||
# region Public methods
|
||||
|
||||
async def _run_until_disconnected(self: 'TelegramClient'):
|
||||
try:
|
||||
|
@ -293,9 +290,6 @@ class UpdateMethods:
|
|||
self._state_cache._pts_date = (pts, date)
|
||||
self.session.catching_up = False
|
||||
|
||||
# endregion
|
||||
|
||||
# region Private methods
|
||||
|
||||
# It is important to not make _handle_update async because we rely on
|
||||
# the order that the updates arrive in to update the pts and date to
|
||||
|
@ -627,8 +621,6 @@ class UpdateMethods:
|
|||
self._log[__name__].exception(
|
||||
'Unhandled exception while getting update difference after reconnect')
|
||||
|
||||
# endregion
|
||||
|
||||
|
||||
class EventBuilderDict:
|
||||
"""
|
||||
|
|
|
@ -88,10 +88,6 @@ def _resize_photo_if_needed(
|
|||
file.seek(before, io.SEEK_SET)
|
||||
|
||||
|
||||
class UploadMethods:
|
||||
|
||||
# region Public methods
|
||||
|
||||
async def send_file(
|
||||
self: 'TelegramClient',
|
||||
entity: 'hints.EntityLike',
|
||||
|
@ -118,223 +114,6 @@ class UploadMethods:
|
|||
comment_to: 'typing.Union[int, types.Message]' = None,
|
||||
ttl: int = None,
|
||||
**kwargs) -> 'types.Message':
|
||||
"""
|
||||
Sends message with the given file to the specified entity.
|
||||
|
||||
.. note::
|
||||
|
||||
If the ``hachoir3`` package (``hachoir`` module) is installed,
|
||||
it will be used to determine metadata from audio and video files.
|
||||
|
||||
If the ``pillow`` package is installed and you are sending a photo,
|
||||
it will be resized to fit within the maximum dimensions allowed
|
||||
by Telegram to avoid ``errors.PhotoInvalidDimensionsError``. This
|
||||
cannot be done if you are sending :tl:`InputFile`, however.
|
||||
|
||||
Arguments
|
||||
entity (`entity`):
|
||||
Who will receive the file.
|
||||
|
||||
file (`str` | `bytes` | `file` | `media`):
|
||||
The file to send, which can be one of:
|
||||
|
||||
* A local file path to an in-disk file. The file name
|
||||
will be the path's base name.
|
||||
|
||||
* A `bytes` byte array with the file's data to send
|
||||
(for example, by using ``text.encode('utf-8')``).
|
||||
A default file name will be used.
|
||||
|
||||
* A bytes `io.IOBase` stream over the file to send
|
||||
(for example, by using ``open(file, 'rb')``).
|
||||
Its ``.name`` property will be used for the file name,
|
||||
or a default if it doesn't have one.
|
||||
|
||||
* An external URL to a file over the internet. This will
|
||||
send the file as "external" media, and Telegram is the
|
||||
one that will fetch the media and send it.
|
||||
|
||||
* A Bot API-like ``file_id``. You can convert previously
|
||||
sent media to file IDs for later reusing with
|
||||
`telethon.utils.pack_bot_file_id`.
|
||||
|
||||
* A handle to an existing file (for example, if you sent a
|
||||
message with media before, you can use its ``message.media``
|
||||
as a file here).
|
||||
|
||||
* A handle to an uploaded file (from `upload_file`).
|
||||
|
||||
* A :tl:`InputMedia` instance. For example, if you want to
|
||||
send a dice use :tl:`InputMediaDice`, or if you want to
|
||||
send a contact use :tl:`InputMediaContact`.
|
||||
|
||||
To send an album, you should provide a list in this parameter.
|
||||
|
||||
If a list or similar is provided, the files in it will be
|
||||
sent as an album in the order in which they appear, sliced
|
||||
in chunks of 10 if more than 10 are given.
|
||||
|
||||
caption (`str`, optional):
|
||||
Optional caption for the sent media message. When sending an
|
||||
album, the caption may be a list of strings, which will be
|
||||
assigned to the files pairwise.
|
||||
|
||||
force_document (`bool`, optional):
|
||||
If left to `False` and the file is a path that ends with
|
||||
the extension of an image file or a video file, it will be
|
||||
sent as such. Otherwise always as a document.
|
||||
|
||||
file_size (`int`, optional):
|
||||
The size of the file to be uploaded if it needs to be uploaded,
|
||||
which will be determined automatically if not specified.
|
||||
|
||||
If the file size can't be determined beforehand, the entire
|
||||
file will be read in-memory to find out how large it is.
|
||||
|
||||
clear_draft (`bool`, optional):
|
||||
Whether the existing draft should be cleared or not.
|
||||
|
||||
progress_callback (`callable`, optional):
|
||||
A callback function accepting two parameters:
|
||||
``(sent bytes, total)``.
|
||||
|
||||
reply_to (`int` | `Message <telethon.tl.custom.message.Message>`):
|
||||
Same as `reply_to` from `send_message`.
|
||||
|
||||
attributes (`list`, optional):
|
||||
Optional attributes that override the inferred ones, like
|
||||
:tl:`DocumentAttributeFilename` and so on.
|
||||
|
||||
thumb (`str` | `bytes` | `file`, optional):
|
||||
Optional JPEG thumbnail (for documents). **Telegram will
|
||||
ignore this parameter** unless you pass a ``.jpg`` file!
|
||||
|
||||
The file must also be small in dimensions and in disk size.
|
||||
Successful thumbnails were files below 20kB and 320x320px.
|
||||
Width/height and dimensions/size ratios may be important.
|
||||
For Telegram to accept a thumbnail, you must provide the
|
||||
dimensions of the underlying media through ``attributes=``
|
||||
with :tl:`DocumentAttributesVideo` or by installing the
|
||||
optional ``hachoir`` dependency.
|
||||
|
||||
|
||||
allow_cache (`bool`, optional):
|
||||
This parameter currently does nothing, but is kept for
|
||||
backward-compatibility (and it may get its use back in
|
||||
the future).
|
||||
|
||||
parse_mode (`object`, optional):
|
||||
See the `TelegramClient.parse_mode
|
||||
<telethon.client.messageparse.MessageParseMethods.parse_mode>`
|
||||
property for allowed values. Markdown parsing will be used by
|
||||
default.
|
||||
|
||||
formatting_entities (`list`, optional):
|
||||
A list of message formatting entities. When provided, the ``parse_mode`` is ignored.
|
||||
|
||||
voice_note (`bool`, optional):
|
||||
If `True` the audio will be sent as a voice note.
|
||||
|
||||
video_note (`bool`, optional):
|
||||
If `True` the video will be sent as a video note,
|
||||
also known as a round video message.
|
||||
|
||||
buttons (`list`, `custom.Button <telethon.tl.custom.button.Button>`, :tl:`KeyboardButton`):
|
||||
The matrix (list of lists), row list or button to be shown
|
||||
after sending the message. This parameter will only work if
|
||||
you have signed in as a bot. You can also pass your own
|
||||
:tl:`ReplyMarkup` here.
|
||||
|
||||
silent (`bool`, optional):
|
||||
Whether the message should notify people with sound or not.
|
||||
Defaults to `False` (send with a notification sound unless
|
||||
the person has the chat muted). Set it to `True` to alter
|
||||
this behaviour.
|
||||
|
||||
background (`bool`, optional):
|
||||
Whether the message should be send in background.
|
||||
|
||||
supports_streaming (`bool`, optional):
|
||||
Whether the sent video supports streaming or not. Note that
|
||||
Telegram only recognizes as streamable some formats like MP4,
|
||||
and others like AVI or MKV will not work. You should convert
|
||||
these to MP4 before sending if you want them to be streamable.
|
||||
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.
|
||||
|
||||
comment_to (`int` | `Message <telethon.tl.custom.message.Message>`, optional):
|
||||
Similar to ``reply_to``, but replies in the linked group of a
|
||||
broadcast channel instead (effectively leaving a "comment to"
|
||||
the specified message).
|
||||
|
||||
This parameter takes precedence over ``reply_to``. If there is
|
||||
no linked chat, `telethon.errors.sgIdInvalidError` is raised.
|
||||
|
||||
ttl (`int`. optional):
|
||||
The Time-To-Live of the file (also known as "self-destruct timer"
|
||||
or "self-destructing media"). If set, files can only be viewed for
|
||||
a short period of time before they disappear from the message
|
||||
history automatically.
|
||||
|
||||
The value must be at least 1 second, and at most 60 seconds,
|
||||
otherwise Telegram will ignore this parameter.
|
||||
|
||||
Not all types of media can be used with this parameter, such
|
||||
as text documents, which will fail with ``TtlMediaInvalidError``.
|
||||
|
||||
Returns
|
||||
The `Message <telethon.tl.custom.message.Message>` (or messages)
|
||||
containing the sent file, or messages if a list of them was passed.
|
||||
|
||||
Example
|
||||
.. code-block:: python
|
||||
|
||||
# Normal files like photos
|
||||
await client.send_file(chat, '/my/photos/me.jpg', caption="It's me!")
|
||||
# or
|
||||
await client.send_message(chat, "It's me!", file='/my/photos/me.jpg')
|
||||
|
||||
# Voice notes or round videos
|
||||
await client.send_file(chat, '/my/songs/song.mp3', voice_note=True)
|
||||
await client.send_file(chat, '/my/videos/video.mp4', video_note=True)
|
||||
|
||||
# Custom thumbnails
|
||||
await client.send_file(chat, '/my/documents/doc.txt', thumb='photo.jpg')
|
||||
|
||||
# Only documents
|
||||
await client.send_file(chat, '/my/photos/photo.png', force_document=True)
|
||||
|
||||
# Albums
|
||||
await client.send_file(chat, [
|
||||
'/my/photos/holiday1.jpg',
|
||||
'/my/photos/holiday2.jpg',
|
||||
'/my/drawings/portrait.png'
|
||||
])
|
||||
|
||||
# Printing upload progress
|
||||
def callback(current, total):
|
||||
print('Uploaded', current, 'out of', total,
|
||||
'bytes: {:.2%}'.format(current / total))
|
||||
|
||||
await client.send_file(chat, file, progress_callback=callback)
|
||||
|
||||
# Dices, including dart and other future emoji
|
||||
from telethon.tl import types
|
||||
await client.send_file(chat, types.InputMediaDice(''))
|
||||
await client.send_file(chat, types.InputMediaDice('🎯'))
|
||||
|
||||
# Contacts
|
||||
await client.send_file(chat, types.InputMediaContact(
|
||||
phone_number='+34 123 456 789',
|
||||
first_name='Example',
|
||||
last_name='',
|
||||
vcard=''
|
||||
))
|
||||
"""
|
||||
# TODO Properly implement allow_cache to reuse the sha256 of the file
|
||||
# i.e. `None` was used
|
||||
if not file:
|
||||
|
@ -493,80 +272,6 @@ class UploadMethods:
|
|||
key: bytes = None,
|
||||
iv: bytes = None,
|
||||
progress_callback: 'hints.ProgressCallback' = None) -> 'types.TypeInputFile':
|
||||
"""
|
||||
Uploads a file to Telegram's servers, without sending it.
|
||||
|
||||
.. note::
|
||||
|
||||
Generally, you want to use `send_file` instead.
|
||||
|
||||
This method returns a handle (an instance of :tl:`InputFile` or
|
||||
:tl:`InputFileBig`, as required) which can be later used before
|
||||
it expires (they are usable during less than a day).
|
||||
|
||||
Uploading a file will simply return a "handle" to the file stored
|
||||
remotely in the Telegram servers, which can be later used on. This
|
||||
will **not** upload the file to your own chat or any chat at all.
|
||||
|
||||
Arguments
|
||||
file (`str` | `bytes` | `file`):
|
||||
The path of the file, byte array, or stream that will be sent.
|
||||
Note that if a byte array or a stream is given, a filename
|
||||
or its type won't be inferred, and it will be sent as an
|
||||
"unnamed application/octet-stream".
|
||||
|
||||
part_size_kb (`int`, optional):
|
||||
Chunk size when uploading files. The larger, the less
|
||||
requests will be made (up to 512KB maximum).
|
||||
|
||||
file_size (`int`, optional):
|
||||
The size of the file to be uploaded, which will be determined
|
||||
automatically if not specified.
|
||||
|
||||
If the file size can't be determined beforehand, the entire
|
||||
file will be read in-memory to find out how large it is.
|
||||
|
||||
file_name (`str`, optional):
|
||||
The file name which will be used on the resulting InputFile.
|
||||
If not specified, the name will be taken from the ``file``
|
||||
and if this is not a `str`, it will be ``"unnamed"``.
|
||||
|
||||
use_cache (`type`, optional):
|
||||
This parameter currently does nothing, but is kept for
|
||||
backward-compatibility (and it may get its use back in
|
||||
the future).
|
||||
|
||||
key ('bytes', optional):
|
||||
In case of an encrypted upload (secret chats) a key is supplied
|
||||
|
||||
iv ('bytes', optional):
|
||||
In case of an encrypted upload (secret chats) an iv is supplied
|
||||
|
||||
progress_callback (`callable`, optional):
|
||||
A callback function accepting two parameters:
|
||||
``(sent bytes, total)``.
|
||||
|
||||
Returns
|
||||
:tl:`InputFileBig` if the file size is larger than 10MB,
|
||||
`InputSizedFile <telethon.tl.custom.inputsizedfile.InputSizedFile>`
|
||||
(subclass of :tl:`InputFile`) otherwise.
|
||||
|
||||
Example
|
||||
.. code-block:: python
|
||||
|
||||
# Photos as photo and document
|
||||
file = await client.upload_file('photo.jpg')
|
||||
await client.send_file(chat, file) # sends as photo
|
||||
await client.send_file(chat, file, force_document=True) # sends as document
|
||||
|
||||
file.name = 'not a photo.jpg'
|
||||
await client.send_file(chat, file, force_document=True) # document, new name
|
||||
|
||||
# As song or as voice note
|
||||
file = await client.upload_file('song.ogg')
|
||||
await client.send_file(chat, file) # sends as song
|
||||
await client.send_file(chat, file, voice_note=True) # sends as voice note
|
||||
"""
|
||||
if isinstance(file, (types.InputFile, types.InputFileBig)):
|
||||
return file # Already uploaded
|
||||
|
||||
|
@ -661,7 +366,6 @@ class UploadMethods:
|
|||
file_id, part_count, file_name, md5=hash_md5, size=file_size
|
||||
)
|
||||
|
||||
# endregion
|
||||
|
||||
async def _file_to_media(
|
||||
self, file, force_document=False, file_size=None,
|
||||
|
@ -762,5 +466,3 @@ class UploadMethods:
|
|||
ttl_seconds=ttl
|
||||
)
|
||||
return file_handle, media, as_image
|
||||
|
||||
# endregion
|
||||
|
|
|
@ -25,11 +25,7 @@ def _fmt_flood(delay, request, *, early=False, td=datetime.timedelta):
|
|||
)
|
||||
|
||||
|
||||
class UserMethods:
|
||||
async def __call__(self: 'TelegramClient', request, ordered=False, flood_sleep_threshold=None):
|
||||
return await self._call(self._sender, request, ordered=ordered)
|
||||
|
||||
async def _call(self: 'TelegramClient', sender, request, ordered=False, flood_sleep_threshold=None):
|
||||
async def call(self: 'TelegramClient', sender, request, ordered=False, flood_sleep_threshold=None):
|
||||
if flood_sleep_threshold is None:
|
||||
flood_sleep_threshold = self.flood_sleep_threshold
|
||||
requests = (request if utils.is_list_like(request) else (request,))
|
||||
|
@ -130,7 +126,6 @@ class UserMethods:
|
|||
raise ValueError('Request was unsuccessful {} time(s)'
|
||||
.format(attempt))
|
||||
|
||||
# region Public methods
|
||||
|
||||
async def get_me(self: 'TelegramClient', input_peer: bool = False) \
|
||||
-> 'typing.Union[types.User, types.InputPeerUser]':
|
||||
|
@ -171,7 +166,6 @@ class UserMethods:
|
|||
except errors.UnauthorizedError:
|
||||
return None
|
||||
|
||||
@property
|
||||
def _self_id(self: 'TelegramClient') -> typing.Optional[int]:
|
||||
"""
|
||||
Returns the ID of the logged-in user, if known.
|
||||
|
@ -507,9 +501,6 @@ class UserMethods:
|
|||
|
||||
return utils.get_peer_id(peer, add_mark=add_mark)
|
||||
|
||||
# endregion
|
||||
|
||||
# region Private methods
|
||||
|
||||
async def _get_entity_from_string(self: 'TelegramClient', string):
|
||||
"""
|
||||
|
@ -607,5 +598,3 @@ class UserMethods:
|
|||
pass
|
||||
|
||||
return types.InputNotifyPeer(await self.get_input_entity(notify))
|
||||
|
||||
# endregion
|
||||
|
|
Loading…
Reference in New Issue
Block a user