2
0
mirror of https://github.com/pyrogram/pyrogram synced 2025-08-29 13:27:47 +00:00

Merge branch 'develop' into other_start

This commit is contained in:
Elliot Manson 2019-08-04 16:28:20 +03:00 committed by GitHub
commit 31960d0272
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
181 changed files with 2577 additions and 1183 deletions

4
.gitignore vendored
View File

@ -11,6 +11,9 @@ pyrogram/api/all.py
# PyCharm stuff # PyCharm stuff
.idea/ .idea/
# VS Code
.vscode/
# Byte-compiled / optimized / DLL files # Byte-compiled / optimized / DLL files
__pycache__/ __pycache__/
*.py[cod] *.py[cod]
@ -78,6 +81,7 @@ instance/
# Sphinx documentation # Sphinx documentation
docs/_build/ docs/_build/
docs/source/_build
# PyBuilder # PyBuilder
target/ target/

View File

@ -478,7 +478,6 @@ def start():
f.write("\n 0xbc799737: \"pyrogram.api.core.BoolFalse\",") f.write("\n 0xbc799737: \"pyrogram.api.core.BoolFalse\",")
f.write("\n 0x997275b5: \"pyrogram.api.core.BoolTrue\",") f.write("\n 0x997275b5: \"pyrogram.api.core.BoolTrue\",")
f.write("\n 0x56730bcc: \"pyrogram.api.core.Null\",")
f.write("\n 0x1cb5c415: \"pyrogram.api.core.Vector\",") f.write("\n 0x1cb5c415: \"pyrogram.api.core.Vector\",")
f.write("\n 0x73f1f8dc: \"pyrogram.api.core.MsgContainer\",") f.write("\n 0x73f1f8dc: \"pyrogram.api.core.MsgContainer\",")
f.write("\n 0xae500895: \"pyrogram.api.core.FutureSalts\",") f.write("\n 0xae500895: \"pyrogram.api.core.FutureSalts\",")

View File

@ -1364,7 +1364,4 @@ langpack.getLanguage#6a596502 lang_pack:string lang_code:string = LangPackLangua
folders.editPeerFolders#6847d0ab folder_peers:Vector<InputFolderPeer> = Updates; folders.editPeerFolders#6847d0ab folder_peers:Vector<InputFolderPeer> = Updates;
folders.deleteFolder#1c295881 folder_id:int = Updates; folders.deleteFolder#1c295881 folder_id:int = Updates;
// LAYER 103 // LAYER 103
// Ports
channels.exportInvite#c7560885 channel:InputChannel = ExportedChatInvite;

View File

@ -53,6 +53,15 @@ ipPortSecret#37982646 ipv4:int port:int secret:bytes = IpPort;
accessPointRule#4679b65f phone_prefix_rules:string dc_id:int ips:vector<IpPort> = AccessPointRule; accessPointRule#4679b65f phone_prefix_rules:string dc_id:int ips:vector<IpPort> = AccessPointRule;
help.configSimple#5a592a6c date:int expires:int rules:vector<AccessPointRule> = help.ConfigSimple; help.configSimple#5a592a6c date:int expires:int rules:vector<AccessPointRule> = help.ConfigSimple;
// tlsClientHello blocks:vector<TlsBlock> = TlsClientHello;
//
// tlsBlockString data:string = TlsBlock;
// tlsBlockRandom length:int = TlsBlock;
// tlsBlockZero length:int = TlsBlock;
// tlsBlockDomain = TlsBlock;
// tlsBlockGrease seed:int = TlsBlock;
// tlsBlockScope entries:Vector<TlsBlock> = TlsBlock;
---functions--- ---functions---
rpc_drop_answer#58e4a740 req_msg_id:long = RpcDropAnswer; rpc_drop_answer#58e4a740 req_msg_id:long = RpcDropAnswer;

View File

@ -136,6 +136,7 @@ def pyrogram_api():
remove_handler remove_handler
stop_transmission stop_transmission
export_session_string export_session_string
set_parse_mode
""", """,
messages=""" messages="""
Messages Messages
@ -145,7 +146,6 @@ def pyrogram_api():
send_audio send_audio
send_document send_document
send_sticker send_sticker
send_animated_sticker
send_video send_video
send_animation send_animation
send_voice send_voice
@ -189,6 +189,7 @@ def pyrogram_api():
delete_chat_photo delete_chat_photo
set_chat_title set_chat_title
set_chat_description set_chat_description
set_chat_permissions
pin_chat_message pin_chat_message
unpin_chat_message unpin_chat_message
get_chat get_chat
@ -199,10 +200,15 @@ def pyrogram_api():
get_dialogs get_dialogs
iter_dialogs iter_dialogs
get_dialogs_count get_dialogs_count
restrict_chat
update_chat_username update_chat_username
archive_chats archive_chats
unarchive_chats unarchive_chats
add_chat_members
create_channel
create_group
create_supergroup
delete_channel
delete_supergroup
""", """,
users=""" users="""
Users Users
@ -334,6 +340,8 @@ def pyrogram_api():
InlineQuery InlineQuery
InlineQueryResult InlineQueryResult
InlineQueryResultArticle InlineQueryResultArticle
InlineQueryResultPhoto
InlineQueryResultAnimation
""", """,
input_message_content=""" input_message_content="""
InputMessageContent InputMessageContent
@ -412,11 +420,15 @@ def pyrogram_api():
Chat.unban_member Chat.unban_member
Chat.restrict_member Chat.restrict_member
Chat.promote_member Chat.promote_member
Chat.join
Chat.leave
""", """,
user=""" user="""
User User
User.archive User.archive
User.unarchive User.unarchive
User.block
User.unblock
""", """,
callback_query=""" callback_query="""
Callback Query Callback Query

View File

@ -113,4 +113,13 @@ FILE_PART_TOO_BIG The size limit (512 KB) for the content of the file part has b
FILE_PART_EMPTY The file part sent is empty FILE_PART_EMPTY The file part sent is empty
FILE_PART_SIZE_INVALID 512 KB cannot be evenly divided by part_size FILE_PART_SIZE_INVALID 512 KB cannot be evenly divided by part_size
FILE_PART_SIZE_CHANGED The part size is different from the size of one of the previous parts in the same file FILE_PART_SIZE_CHANGED The part size is different from the size of one of the previous parts in the same file
FILE_MIGRATE_X The file is in Data Center No. {x} FILE_MIGRATE_X The file is in Data Center No. {x}
RESULT_TYPE_INVALID The result type is invalid
PHOTO_THUMB_URL_EMPTY The photo thumb URL is empty
PHOTO_THUMB_URL_INVALID The photo thumb URL is invalid
PHOTO_CONTENT_URL_EMPTY The photo content URL is empty
PHOTO_CONTENT_TYPE_INVALID The photo content type is invalid
WEBDOCUMENT_INVALID The web document is invalid
WEBDOCUMENT_URL_EMPTY The web document URL is empty
WEBDOCUMENT_URL_INVALID The web document URL is invalid
WEBDOCUMENT_MIME_INVALID The web document mime type is invalid
1 id message
113 FILE_PART_EMPTY The file part sent is empty
114 FILE_PART_SIZE_INVALID 512 KB cannot be evenly divided by part_size
115 FILE_PART_SIZE_CHANGED The part size is different from the size of one of the previous parts in the same file
116 FILE_MIGRATE_X The file is in Data Center No. {x}
117 RESULT_TYPE_INVALID The result type is invalid
118 PHOTO_THUMB_URL_EMPTY The photo thumb URL is empty
119 PHOTO_THUMB_URL_INVALID The photo thumb URL is invalid
120 PHOTO_CONTENT_URL_EMPTY The photo content URL is empty
121 PHOTO_CONTENT_TYPE_INVALID The photo content type is invalid
122 WEBDOCUMENT_INVALID The web document is invalid
123 WEBDOCUMENT_URL_EMPTY The web document URL is empty
124 WEBDOCUMENT_URL_INVALID The web document URL is invalid
125 WEBDOCUMENT_MIME_INVALID The web document mime type is invalid

View File

@ -144,7 +144,8 @@ I started a client and nothing happens!
--------------------------------------- ---------------------------------------
If you are connecting from Russia, China or Iran :doc:`you need a proxy <topics/proxy>`, because Telegram could be If you are connecting from Russia, China or Iran :doc:`you need a proxy <topics/proxy>`, because Telegram could be
partially or totally blocked in those countries. partially or totally blocked in those countries. More information about this block can be found at
`Wikipedia <https://en.wikipedia.org/wiki/Blocking_Telegram_in_Russia>`_.
Another possible cause might be network issues, either yours or Telegram's. To confirm this, add the following code on Another possible cause might be network issues, either yours or Telegram's. To confirm this, add the following code on
the top of your script and run it again. You should see some error mentioning a socket timeout or an unreachable network the top of your script and run it again. You should see some error mentioning a socket timeout or an unreachable network
@ -161,9 +162,9 @@ fails or not.
What are the IP addresses of Telegram Data Centers? What are the IP addresses of Telegram Data Centers?
--------------------------------------------------- ---------------------------------------------------
The Telegram cloud is currently composed by a decentralized, multi-DC infrastructure (each of which can work The Telegram cloud is currently composed by a decentralized, multi-DC infrastructure (currently 5 DCs, each of which can
independently) spread in 5 different locations. However, some of the less busy DCs have been lately dismissed and their work independently) spread in different locations worldwide. However, some of the less busy DCs have been lately
IP addresses are now kept as aliases. dismissed and their IP addresses are now kept as aliases to the nearest one.
.. csv-table:: Production Environment .. csv-table:: Production Environment
:header: ID, Location, IPv4, IPv6 :header: ID, Location, IPv4, IPv6
@ -191,7 +192,6 @@ IP addresses are now kept as aliases.
Thanks to `@FrayxRulez <https://t.me/tgbetachat/104921>`_ for telling about alias DCs. Thanks to `@FrayxRulez <https://t.me/tgbetachat/104921>`_ for telling about alias DCs.
I want to migrate my account from DCX to DCY. I want to migrate my account from DCX to DCY.
--------------------------------------------- ---------------------------------------------
@ -245,9 +245,13 @@ The error in question is ``[400 PEER_ID_INVALID]``, and could mean several thing
- The chat id you tried to use is simply wrong, double check it. - The chat id you tried to use is simply wrong, double check it.
- The chat id refers to a group or channel you are not a member of. - The chat id refers to a group or channel you are not a member of.
- The chat id refers to a user you have't seen yet (from contacts, groups in common, forwarded messages or private
chats).
- The chat id argument you passed is in form of a string; you have to convert it into an integer with ``int(chat_id)``. - The chat id argument you passed is in form of a string; you have to convert it into an integer with ``int(chat_id)``.
- The chat id refers to a user your current session haven't met yet.
About the last point: in order for you to meet a user and thus communicate with them, you should ask yourself how to
contact people using official apps. The answer is the same for Pyrogram too and involves normal usages such as searching
for usernames, meet them in a common group, have their phone contacts saved, getting a message mentioning them (either a
forward or a mention in the message text).
UnicodeEncodeError: '<encoding>' codec can't encode … UnicodeEncodeError: '<encoding>' codec can't encode …
----------------------------------------------------- -----------------------------------------------------
@ -257,6 +261,14 @@ shows up when you try to print something and has very little to do with Pyrogram
your own terminal. To fix it, either find a way to change the encoding settings of your terminal to UTF-8 or switch to a your own terminal. To fix it, either find a way to change the encoding settings of your terminal to UTF-8 or switch to a
better terminal altogether. better terminal altogether.
Uploading with URLs gives error WEBPAGE_CURL_FAILED
---------------------------------------------------
When uploading media files using an URL, the server automatically tries to download the media and uploads it to the
Telegram cloud. This error usually happens in case the provided URL is not publicly accessible by Telegram itself or the
media exceeds 20 MB in size. In such cases, your only option is to download the media yourself and upload from your
local machine.
My verification code expires immediately! My verification code expires immediately!
----------------------------------------- -----------------------------------------

View File

@ -45,8 +45,9 @@ Welcome to Pyrogram
topics/tgcrypto topics/tgcrypto
topics/storage-engines topics/storage-engines
topics/text-formatting topics/text-formatting
topics/serialize topics/serializing
topics/proxy topics/proxy
topics/scheduling
topics/bots-interaction topics/bots-interaction
topics/mtproto-vs-botapi topics/mtproto-vs-botapi
topics/debugging topics/debugging

View File

@ -0,0 +1,87 @@
Scheduling Tasks
================
Scheduling tasks means executing one or more functions periodically at pre-defined intervals or after a delay. This is
useful, for example, to send recurring messages to specific chats or users.
Since there's no built-in task scheduler in Pyrogram, this page will only show examples on how to integrate Pyrogram
with the main Python schedule libraries such as ``schedule`` and ``apscheduler``. For more detailed information, you can
visit and learn from each library documentation.
Using ``schedule``
------------------
- Install with ``pip3 install schedule``
- Documentation: https://schedule.readthedocs.io
.. code-block:: python
import time
import schedule
from pyrogram import Client
app = Client("my_account")
def job():
app.send_message("me", "Hi!")
schedule.every(3).seconds.do(job)
with app:
while True:
schedule.run_pending()
time.sleep(1)
Using ``apscheduler``
---------------------
- Install with ``pip3 install apscheduler``
- Documentation: https://apscheduler.readthedocs.io
.. code-block:: python
from apscheduler.schedulers.background import BackgroundScheduler
from pyrogram import Client
app = Client("my_account")
def job():
app.send_message("me", "Hi!")
scheduler = BackgroundScheduler()
scheduler.add_job(job, "interval", seconds=3)
scheduler.start()
app.run()
``apscheduler`` does also support async code, here's an example with
`Pyrogram Asyncio <https://docs.pyrogram.org/intro/install.html#asynchronous>`_:
.. code-block:: python
from apscheduler.schedulers.asyncio import AsyncIOScheduler
from pyrogram import Client
app = Client("my_account")
async def job():
await app.send_message("me", "Hi!")
scheduler = AsyncIOScheduler()
scheduler.add_job(job, "interval", seconds=3)
scheduler.start()
app.run()

View File

@ -316,9 +316,9 @@ attribute. Here's an example:
Unloading Unloading
^^^^^^^^^ ^^^^^^^^^
In order to unload a plugin, or any other handler, all you need to do is obtain a reference to it by importing the In order to unload a plugin, all you need to do is obtain a reference to it by importing the relevant module and call
relevant module and call :meth:`~pyrogram.Client.remove_handler` Client's method with your function :meth:`~pyrogram.Client.remove_handler` Client's method with your function's *handler* special attribute preceded by the
name preceded by the star ``*`` operator as argument. Example: star ``*`` operator as argument. Example:
- ``main.py`` - ``main.py``
@ -328,14 +328,14 @@ name preceded by the star ``*`` operator as argument. Example:
... ...
app.remove_handler(*echo) app.remove_handler(*echo.handler)
The star ``*`` operator is used to unpack the tuple into positional arguments so that *remove_handler* will receive The star ``*`` operator is used to unpack the tuple into positional arguments so that *remove_handler* will receive
exactly what is needed. The same could have been achieved with: exactly what is needed. The same could have been achieved with:
.. code-block:: python .. code-block:: python
handler, group = echo handler, group = echo.handler
app.remove_handler(handler, group) app.remove_handler(handler, group)
Loading Loading
@ -352,4 +352,4 @@ using :meth:`~pyrogram.Client.add_handler` instead. Example:
... ...
app.add_handler(*echo) app.add_handler(*echo.handler)

View File

@ -19,8 +19,7 @@
from importlib import import_module from importlib import import_module
from .all import objects from .all import objects
from .core.tl_object import TLObject
for k, v in objects.items(): for k, v in objects.items():
path, name = v.rsplit(".", 1) path, name = v.rsplit(".", 1)
TLObject.all[k] = getattr(import_module(path), name) objects[k] = getattr(import_module(path), name)

View File

@ -22,8 +22,5 @@ from .gzip_packed import GzipPacked
from .list import List from .list import List
from .message import Message from .message import Message
from .msg_container import MsgContainer from .msg_container import MsgContainer
from .primitives import ( from .primitives import *
Bool, BoolTrue, BoolFalse, Bytes, Double,
Int, Long, Int128, Int256, Null, String, Vector
)
from .tl_object import TLObject from .tl_object import TLObject

View File

@ -16,10 +16,11 @@
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>. # along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
from .bool import Bool, BoolTrue, BoolFalse from .bool import Bool, BoolFalse, BoolTrue
from .bytes import Bytes from .bytes import Bytes
from .double import Double from .double import Double
from .int import Int, Long, Int128, Int256 from .int import Int, Long, Int128, Int256
from .null import Null
from .string import String from .string import String
from .vector import Vector from .vector import Vector
__all__ = ["Bool", "BoolFalse", "BoolTrue", "Bytes", "Double", "Int", "Long", "Int128", "Int256", "String", "Vector"]

View File

@ -20,17 +20,17 @@ from collections import OrderedDict
from io import BytesIO from io import BytesIO
from json import dumps from json import dumps
from ..all import objects
class TLObject: class TLObject:
all = {}
__slots__ = [] __slots__ = []
QUALNAME = "Base" QUALNAME = "Base"
@staticmethod @staticmethod
def read(b: BytesIO, *args): # TODO: Rename b -> data def read(b: BytesIO, *args): # TODO: Rename b -> data
return TLObject.all[int.from_bytes(b.read(4), "little")].read(b, *args) return objects[int.from_bytes(b.read(4), "little")].read(b, *args)
def write(self, *args) -> bytes: def write(self, *args) -> bytes:
pass pass

View File

@ -174,6 +174,17 @@ class Client(Methods, BaseClient):
download_media, ...) are less prone to throw FloodWait exceptions. download_media, ...) are less prone to throw FloodWait exceptions.
Only available for users, bots will ignore this parameter. Only available for users, bots will ignore this parameter.
Defaults to False (normal session). Defaults to False (normal session).
Example:
.. code-block:: python
from pyrogram import Client
app = Client("my_account")
with app:
app.send_message("me", "Hi!")
""" """
terms_of_service_displayed = False terms_of_service_displayed = False
@ -619,11 +630,28 @@ class Client(Methods, BaseClient):
return self return self
def start(self): def start(self):
"""Start the Client. """Start the client.
This method connects the client to Telegram and, in case of new sessions, automatically manages the full login
process using an interactive prompt (by default).
Has no parameters.
Raises: Raises:
RPCError: In case of a Telegram RPC error. ConnectionError: In case you try to start an already started client.
ConnectionError: In case you try to start an already started Client.
Example:
.. code-block:: python
:emphasize-lines: 4
from pyrogram import Client
app = Client("my_account")
app.start()
... # Call API methods
app.stop()
""" """
if self.is_started: if self.is_started:
raise ConnectionError("Client has already been started") raise ConnectionError("Client has already been started")
@ -696,8 +724,25 @@ class Client(Methods, BaseClient):
def stop(self): def stop(self):
"""Stop the Client. """Stop the Client.
This method disconnects the client from Telegram and stops the underlying tasks.
Has no parameters.
Raises: Raises:
ConnectionError: In case you try to stop an already stopped Client. ConnectionError: In case you try to stop an already stopped client.
Example:
.. code-block:: python
:emphasize-lines: 8
from pyrogram import Client
app = Client("my_account")
app.start()
... # Call API methods
app.stop()
""" """
if not self.is_started: if not self.is_started:
raise ConnectionError("Client is already stopped") raise ConnectionError("Client is already stopped")
@ -738,64 +783,125 @@ class Client(Methods, BaseClient):
def restart(self): def restart(self):
"""Restart the Client. """Restart the Client.
This method will first call :meth:`~Client.stop` and then :meth:`~Client.start` in a row in order to restart
a client using a single method.
Has no parameters.
Raises: Raises:
ConnectionError: In case you try to restart a stopped Client. ConnectionError: In case you try to restart a stopped Client.
Example:
.. code-block:: python
:emphasize-lines: 8
from pyrogram import Client
app = Client("my_account")
app.start()
... # Call API methods
app.restart()
... # Call other API methods
app.stop()
""" """
self.stop() self.stop()
self.start() self.start()
def idle(self, stop_signals: tuple = (SIGINT, SIGTERM, SIGABRT)): @staticmethod
"""Block the main script execution until a signal (e.g.: from CTRL+C) is received. def idle(stop_signals: tuple = (SIGINT, SIGTERM, SIGABRT)):
Once the signal is received, the client will automatically stop and the main script will continue its execution. """Block the main script execution until a signal is received.
This is used after starting one or more clients and is useful for event-driven applications only, that are, This static method will run an infinite loop in order to block the main script execution and prevent it from
applications which react upon incoming Telegram updates through handlers, rather than executing a set of methods exiting while having client(s) that are still running in the background.
sequentially.
The way Pyrogram works, will keep your handlers in a pool of workers, which are executed concurrently outside It is useful for event-driven application only, that are, applications which react upon incoming Telegram
the main script; calling idle() will ensure the client(s) will be kept alive by not letting the main script to updates through handlers, rather than executing a set of methods sequentially.
end, until you decide to quit.
The way Pyrogram works, it will keep your handlers in a pool of worker threads, which are executed concurrently
outside the main thread; calling idle() will ensure the client(s) will be kept alive by not letting the main
script to end, until you decide to quit.
Once a signal is received (e.g.: from CTRL+C) the inner infinite loop will break and your main script will
continue. Don't forget to call :meth:`~Client.stop` for each running client before the script ends.
Parameters: Parameters:
stop_signals (``tuple``, *optional*): stop_signals (``tuple``, *optional*):
Iterable containing signals the signal handler will listen to. Iterable containing signals the signal handler will listen to.
Defaults to (SIGINT, SIGTERM, SIGABRT). Defaults to *(SIGINT, SIGTERM, SIGABRT)*.
Example:
.. code-block:: python
:emphasize-lines: 13
from pyrogram import Client
app1 = Client("account1")
app2 = Client("account2")
app3 = Client("account3")
... # Set handlers up
app1.start()
app2.start()
app3.start()
Client.idle()
app1.stop()
app2.stop()
app3.stop()
""" """
# TODO: Maybe make this method static and don't automatically stop
def signal_handler(*args): def signal_handler(*args):
self.is_idle = False Client.is_idling = False
for s in stop_signals: for s in stop_signals:
signal(s, signal_handler) signal(s, signal_handler)
self.is_idle = True Client.is_idling = True
while self.is_idle: while Client.is_idling:
time.sleep(1) time.sleep(1)
self.stop()
def run(self): def run(self):
"""Start the Client and automatically idle the main script. """Start the client, idle the main script and finally stop the client.
This is a convenience method that literally just calls :meth:`~Client.start` and :meth:`~Client.idle`. It makes This is a convenience method that calls :meth:`~Client.start`, :meth:`~Client.idle` and :meth:`~Client.stop` in
running a client less verbose, but is not suitable in case you want to run more than one client in a single main sequence. It makes running a client less verbose, but is not suitable in case you want to run more than one
script, since :meth:`~Client.idle` will block. client in a single main script, since idle() will block after starting the own client.
Has no parameters.
Raises: Raises:
RPCError: In case of a Telegram RPC error. ConnectionError: In case you try to run an already started client.
Example:
.. code-block:: python
:emphasize-lines: 7
from pyrogram import Client
app = Client("my_account")
... # Set handlers up
app.run()
""" """
self.start() self.start()
self.idle() Client.idle()
self.stop()
def add_handler(self, handler: Handler, group: int = 0): def add_handler(self, handler: Handler, group: int = 0):
"""Register an update handler. """Register an update handler.
You can register multiple handlers, but at most one handler within a group You can register multiple handlers, but at most one handler within a group will be used for a single update.
will be used for a single update. To handle the same update more than once, register To handle the same update more than once, register your handler using a different group id (lower group id
your handler using a different group id (lower group id == higher priority). == higher priority). This mechanism is explained in greater details at
:doc:`More on Updates <../../topics/more-on-updates>`.
Parameters: Parameters:
handler (``Handler``): handler (``Handler``):
@ -805,7 +911,22 @@ class Client(Methods, BaseClient):
The group identifier, defaults to 0. The group identifier, defaults to 0.
Returns: Returns:
``tuple``: A tuple consisting of (handler, group). ``tuple``: A tuple consisting of *(handler, group)*.
Example:
.. code-block:: python
:emphasize-lines: 8
from pyrogram import Client, MessageHandler
def dump(client, message):
print(message)
app = Client("my_account")
app.add_handler(MessageHandler(dump))
app.run()
""" """
if isinstance(handler, DisconnectHandler): if isinstance(handler, DisconnectHandler):
self.disconnect_handler = handler.callback self.disconnect_handler = handler.callback
@ -817,9 +938,8 @@ class Client(Methods, BaseClient):
def remove_handler(self, handler: Handler, group: int = 0): def remove_handler(self, handler: Handler, group: int = 0):
"""Remove a previously-registered update handler. """Remove a previously-registered update handler.
Make sure to provide the right group that the handler was added in. You can use Make sure to provide the right group where the handler was added in. You can use the return value of the
the return value of the :meth:`~Client.add_handler` method, a tuple of (handler, group), and :meth:`~Client.add_handler` method, a tuple of *(handler, group)*, and pass it directly.
pass it directly.
Parameters: Parameters:
handler (``Handler``): handler (``Handler``):
@ -827,6 +947,24 @@ class Client(Methods, BaseClient):
group (``int``, *optional*): group (``int``, *optional*):
The group identifier, defaults to 0. The group identifier, defaults to 0.
Example:
.. code-block:: python
:emphasize-lines: 11
from pyrogram import Client, MessageHandler
def dump(client, message):
print(message)
app = Client("my_account")
handler = app.add_handler(MessageHandler(dump))
# Starred expression to unpack (handler, group)
app.remove_handler(*handler)
app.run()
""" """
if isinstance(handler, DisconnectHandler): if isinstance(handler, DisconnectHandler):
self.disconnect_handler = None self.disconnect_handler = None
@ -835,10 +973,109 @@ class Client(Methods, BaseClient):
def stop_transmission(self): def stop_transmission(self):
"""Stop downloading or uploading a file. """Stop downloading or uploading a file.
Must be called inside a progress callback function.
This method must be called inside a progress callback function in order to stop the transmission at the
desired time. The progress callback is called every time a file chunk is uploaded/downloaded.
Has no parameters.
Example:
.. code-block:: python
:emphasize-lines: 9
from pyrogram import Client
app = Client("my_account")
# Example to stop transmission once the upload progress reaches 50%
# Useless in practice, but shows how to stop on command
def progress(client, current, total):
if (current * 100 / total) > 50:
client.stop_transmission()
with app:
app.send_document("me", "files.zip", progress=progress)
""" """
raise Client.StopTransmission raise Client.StopTransmission
def export_session_string(self):
"""Export the current authorized session as a serialized string.
Session strings are useful for storing in-memory authorized sessions in a portable, serialized string.
More detailed information about session strings can be found at the dedicated page of
:doc:`Storage Engines <../../topics/storage-engines>`.
Has no parameters.
Returns:
``str``: The session serialized into a printable, url-safe string.
Example:
.. code-block:: python
:emphasize-lines: 6
from pyrogram import Client
app = Client("my_account")
with app:
print(app.export_session_string())
"""
return self.storage.export_session_string()
def set_parse_mode(self, parse_mode: Union[str, None] = "combined"):
"""Set the parse mode to be used globally by the client.
When setting the parse mode with this method, all other methods having a *parse_mode* parameter will follow the
global value by default. The default value *"combined"* enables both Markdown and HTML styles to be used and
combined together.
Parameters:
parse_mode (``str``):
The new parse mode, can be any of: *"combined"*, for the default combined mode. *"markdown"* or *"md"*
to force Markdown-only styles. *"html"* to force HTML-only styles. *None* to disable the parser
completely.
Raises:
ValueError: In case the provided *parse_mode* is not a valid parse mode.
Example:
.. code-block:: python
:emphasize-lines: 10,14,18,22
from pyrogram import Client
app = Client("my_account")
with app:
# Default combined mode: Markdown + HTML
app.send_message("haskell", "1. **markdown** and <i>html</i>")
# Force Markdown-only, HTML is disabled
app.set_parse_mode("markdown")
app.send_message("haskell", "2. **markdown** and <i>html</i>")
# Force HTML-only, Markdown is disabled
app.set_parse_mode("html")
app.send_message("haskell", "3. **markdown** and <i>html</i>")
# Disable the parser completely
app.set_parse_mode(None)
app.send_message("haskell", "4. **markdown** and <i>html</i>")
# Bring back the default combined mode
app.set_parse_mode()
app.send_message("haskell", "5. **markdown** and <i>html</i>")
"""
if parse_mode not in self.PARSE_MODES:
raise ValueError('parse_mode must be one of {} or None. Not "{}"'.format(
", ".join('"{}"'.format(m) for m in self.PARSE_MODES[:-1]),
parse_mode
))
self.parse_mode = parse_mode
def authorize_bot(self): def authorize_bot(self):
try: try:
r = self.send( r = self.send(
@ -1128,7 +1365,7 @@ class Client(Methods, BaseClient):
access_hash = 0 access_hash = 0
peer_type = "group" peer_type = "group"
elif isinstance(peer, (types.Channel, types.ChannelForbidden)): elif isinstance(peer, (types.Channel, types.ChannelForbidden)):
peer_id = int("-100" + str(peer.id)) peer_id = utils.get_channel_id(peer.id)
access_hash = peer.access_hash access_hash = peer.access_hash
username = getattr(peer, "username", None) username = getattr(peer, "username", None)
@ -1244,7 +1481,7 @@ class Client(Methods, BaseClient):
try: try:
diff = self.send( diff = self.send(
functions.updates.GetChannelDifference( functions.updates.GetChannelDifference(
channel=self.resolve_peer(int("-100" + str(channel_id))), channel=self.resolve_peer(utils.get_channel_id(channel_id)),
filter=types.ChannelMessagesFilter( filter=types.ChannelMessagesFilter(
ranges=[types.MessageRange( ranges=[types.MessageRange(
min_id=update.message.id, min_id=update.message.id,
@ -1420,7 +1657,7 @@ class Client(Methods, BaseClient):
]) ])
if session_empty: if session_empty:
self.storage.dc_id = 1 self.storage.dc_id = 4
self.storage.date = 0 self.storage.date = 0
self.storage.test_mode = self.test_mode self.storage.test_mode = self.test_mode
@ -1456,7 +1693,7 @@ class Client(Methods, BaseClient):
for name in vars(module).keys(): for name in vars(module).keys():
# noinspection PyBroadException # noinspection PyBroadException
try: try:
handler, group = getattr(module, name).pyrogram_plugin handler, group = getattr(module, name).handler
if isinstance(handler, Handler) and isinstance(group, int): if isinstance(handler, Handler) and isinstance(group, int):
self.add_handler(handler, group) self.add_handler(handler, group)
@ -1491,7 +1728,7 @@ class Client(Methods, BaseClient):
for name in handlers: for name in handlers:
# noinspection PyBroadException # noinspection PyBroadException
try: try:
handler, group = getattr(module, name).pyrogram_plugin handler, group = getattr(module, name).handler
if isinstance(handler, Handler) and isinstance(group, int): if isinstance(handler, Handler) and isinstance(group, int):
self.add_handler(handler, group) self.add_handler(handler, group)
@ -1529,7 +1766,7 @@ class Client(Methods, BaseClient):
for name in handlers: for name in handlers:
# noinspection PyBroadException # noinspection PyBroadException
try: try:
handler, group = getattr(module, name).pyrogram_plugin handler, group = getattr(module, name).handler
if isinstance(handler, Handler) and isinstance(group, int): if isinstance(handler, Handler) and isinstance(group, int):
self.remove_handler(handler, group) self.remove_handler(handler, group)
@ -1632,33 +1869,38 @@ class Client(Methods, BaseClient):
except KeyError: except KeyError:
raise PeerIdInvalid raise PeerIdInvalid
if peer_id > 0: peer_type = utils.get_type(peer_id)
if peer_type == "user":
self.fetch_peers( self.fetch_peers(
self.send( self.send(
functions.users.GetUsers( functions.users.GetUsers(
id=[types.InputUser( id=[
user_id=peer_id, types.InputUser(
access_hash=0 user_id=peer_id,
)] access_hash=0
)
]
) )
) )
) )
elif peer_type == "chat":
self.send(
functions.messages.GetChats(
id=[-peer_id]
)
)
else: else:
if str(peer_id).startswith("-100"): self.send(
self.send( functions.channels.GetChannels(
functions.channels.GetChannels( id=[
id=[types.InputChannel( types.InputChannel(
channel_id=int(str(peer_id)[4:]), channel_id=utils.get_channel_id(peer_id),
access_hash=0 access_hash=0
)] )
) ]
)
else:
self.send(
functions.messages.GetChats(
id=[-peer_id]
)
) )
)
try: try:
return self.storage.get_peer_by_id(peer_id) return self.storage.get_peer_by_id(peer_id)
@ -1693,23 +1935,22 @@ class Client(Methods, BaseClient):
In case a file part expired, pass the file_id and the file_part to retry uploading that specific chunk. In case a file part expired, pass the file_id and the file_part to retry uploading that specific chunk.
progress (``callable``, *optional*): progress (``callable``, *optional*):
Pass a callback function to view the upload progress. Pass a callback function to view the file transmission progress.
The function must take *(client, current, total, \*args)* as positional arguments (look at the section The function must take *(current, total)* as positional arguments (look at Other Parameters below for a
below for a detailed description). detailed description) and will be called back each time a new file chunk has been successfully
transmitted.
progress_args (``tuple``, *optional*): progress_args (``tuple``, *optional*):
Extra custom arguments for the progress callback function. Useful, for example, if you want to pass Extra custom arguments for the progress callback function.
a chat_id and a message_id in order to edit a message with the updated progress. You can pass anything you need to be available in the progress callback scope; for example, a Message
object or a Client instance in order to edit the message with the updated progress status.
Other Parameters: Other Parameters:
client (:obj:`Client`):
The Client itself, useful when you want to call other API methods inside the callback function.
current (``int``): current (``int``):
The amount of bytes uploaded so far. The amount of bytes transmitted so far.
total (``int``): total (``int``):
The size of the file. The total size of the file.
*args (``tuple``, *optional*): *args (``tuple``, *optional*):
Extra custom arguments as defined in the *progress_args* parameter. Extra custom arguments as defined in the *progress_args* parameter.
@ -2023,11 +2264,3 @@ class Client(Methods, BaseClient):
if extensions: if extensions:
return extensions.split(" ")[0] return extensions.split(" ")[0]
def export_session_string(self):
"""Export the current session as serialized string.
Returns:
``str``: The session serialized into a printable, url-safe string.
"""
return self.storage.export_session_string()

View File

@ -59,6 +59,8 @@ class BaseClient:
WORKDIR = PARENT_DIR WORKDIR = PARENT_DIR
CONFIG_FILE = PARENT_DIR / "config.ini" CONFIG_FILE = PARENT_DIR / "config.ini"
PARSE_MODES = ["combined", "markdown", "md", "html", None]
MEDIA_TYPE_ID = { MEDIA_TYPE_ID = {
0: "photo_thumbnail", 0: "photo_thumbnail",
1: "chat_photo", 1: "chat_photo",
@ -87,19 +89,21 @@ class BaseClient:
mime_types_to_extensions[mime_type] = " ".join(extensions) mime_types_to_extensions[mime_type] = " ".join(extensions)
is_idling = False
def __init__(self): def __init__(self):
self.storage = None self.storage = None
self.rnd_id = MsgId self.rnd_id = MsgId
self.parser = Parser(self) self.parser = Parser(self)
self.parse_mode = "combined"
self.session = None self.session = None
self.media_sessions = {} self.media_sessions = {}
self.media_sessions_lock = Lock() self.media_sessions_lock = Lock()
self.is_started = None self.is_started = None
self.is_idle = None
self.takeout_id = None self.takeout_id = None

View File

@ -166,8 +166,13 @@ class Dispatcher:
args = None args = None
if isinstance(handler, handler_type): if isinstance(handler, handler_type):
if handler.check(parsed_update): try:
args = (parsed_update,) if handler.check(parsed_update):
args = (parsed_update,)
except Exception as e:
log.error(e, exc_info=True)
continue
elif isinstance(handler, RawUpdateHandler): elif isinstance(handler, RawUpdateHandler):
args = (update, users, chats) args = (update, users, chats)

View File

@ -18,10 +18,11 @@
import base64 import base64
import struct import struct
from typing import Union, List from typing import List
from typing import Union
import pyrogram import pyrogram
from pyrogram.api.types import PeerUser, PeerChat, PeerChannel
from . import BaseClient from . import BaseClient
from ...api import types from ...api import types
@ -30,10 +31,17 @@ def decode(s: str) -> bytes:
s = base64.urlsafe_b64decode(s + "=" * (-len(s) % 4)) s = base64.urlsafe_b64decode(s + "=" * (-len(s) % 4))
r = b"" r = b""
assert s[-1] == 2 try:
assert s[-1] == 2
skip = 1
except AssertionError:
assert s[-2] == 22
assert s[-1] == 4
skip = 2
i = 0 i = 0
while i < len(s) - 1:
while i < len(s) - skip:
if s[i] != 0: if s[i] != 0:
r += bytes([s[i]]) r += bytes([s[i]])
else: else:
@ -49,7 +57,7 @@ def encode(s: bytes) -> str:
r = b"" r = b""
n = 0 n = 0
for i in s + bytes([2]): for i in s + bytes([22]) + bytes([4]):
if i == 0: if i == 0:
n += 1 n += 1
else: else:
@ -62,23 +70,6 @@ def encode(s: bytes) -> str:
return base64.urlsafe_b64encode(r).decode().rstrip("=") return base64.urlsafe_b64encode(r).decode().rstrip("=")
def get_peer_id(input_peer) -> int:
return (
input_peer.user_id if isinstance(input_peer, types.InputPeerUser)
else -input_peer.chat_id if isinstance(input_peer, types.InputPeerChat)
else int("-100" + str(input_peer.channel_id))
)
def get_input_peer(peer_id: int, access_hash: int):
return (
types.InputPeerUser(user_id=peer_id, access_hash=access_hash) if peer_id > 0
else types.InputPeerChannel(channel_id=int(str(peer_id)[4:]), access_hash=access_hash)
if (str(peer_id).startswith("-100") and access_hash)
else types.InputPeerChat(chat_id=-peer_id)
)
def get_offset_date(dialogs): def get_offset_date(dialogs):
for m in reversed(dialogs.messages): for m in reversed(dialogs.messages):
if isinstance(m, types.MessageEmpty): if isinstance(m, types.MessageEmpty):
@ -183,7 +174,7 @@ def parse_deleted_messages(client, update) -> List["pyrogram.Message"]:
pyrogram.Message( pyrogram.Message(
message_id=message, message_id=message,
chat=pyrogram.Chat( chat=pyrogram.Chat(
id=int("-100" + str(channel_id)), id=get_channel_id(channel_id),
type="channel", type="channel",
client=client client=client
) if channel_id is not None else None, ) if channel_id is not None else None,
@ -203,3 +194,39 @@ def unpack_inline_message_id(inline_message_id: str) -> types.InputBotInlineMess
id=r[1], id=r[1],
access_hash=r[2] access_hash=r[2]
) )
MIN_CHANNEL_ID = -1002147483647
MAX_CHANNEL_ID = -1000000000000
MIN_CHAT_ID = -2147483647
MAX_USER_ID = 2147483647
def get_peer_id(peer: Union[PeerUser, PeerChat, PeerChannel]) -> int:
if isinstance(peer, PeerUser):
return peer.user_id
if isinstance(peer, PeerChat):
return -peer.chat_id
if isinstance(peer, PeerChannel):
return MAX_CHANNEL_ID - peer.channel_id
raise ValueError("Peer type invalid: {}".format(peer))
def get_type(peer_id: int) -> str:
if peer_id < 0:
if MIN_CHAT_ID <= peer_id:
return "chat"
if MIN_CHANNEL_ID <= peer_id < MAX_CHANNEL_ID:
return "channel"
elif 0 < peer_id <= MAX_USER_ID:
return "user"
raise ValueError("Peer id invalid: {}".format(peer_id))
def get_channel_id(peer_id: int) -> int:
return MAX_CHANNEL_ID - peer_id

View File

@ -136,7 +136,7 @@ class Filters:
poll = create(lambda _, m: m.poll, "PollFilter") poll = create(lambda _, m: m.poll, "PollFilter")
"""Filter messages that contain :obj:`Poll` objects.""" """Filter messages that contain :obj:`Poll` objects."""
private = create(lambda _, m: bool(m.chat and m.chat.type == "private"), "PrivateFilter") private = create(lambda _, m: bool(m.chat and m.chat.type in {"private", "bot"}), "PrivateFilter")
"""Filter messages sent in private chats.""" """Filter messages sent in private chats."""
group = create(lambda _, m: bool(m.chat and m.chat.type in {"group", "supergroup"}), "GroupFilter") group = create(lambda _, m: bool(m.chat and m.chat.type in {"group", "supergroup"}), "GroupFilter")
@ -240,6 +240,7 @@ class Filters:
def func(flt, message): def func(flt, message):
text = message.text or message.caption text = message.text or message.caption
message.command = None
if text: if text:
for p in flt.p: for p in flt.p:
@ -272,11 +273,15 @@ class Filters:
RegEx flags. RegEx flags.
""" """
def f(_, m): def func(flt, message):
m.matches = [i for i in _.p.finditer(m.text or m.caption or "")] text = message.text or message.caption
return bool(m.matches)
return create(f, "RegexFilter", p=re.compile(pattern, flags)) if text:
message.matches = list(flt.p.finditer(text)) or None
return bool(message.matches)
return create(func, "RegexFilter", p=re.compile(pattern, flags))
# noinspection PyPep8Naming # noinspection PyPep8Naming
class user(Filter, set): class user(Filter, set):

View File

@ -56,8 +56,14 @@ class AnswerCallbackQuery(BaseClient):
Returns: Returns:
``bool``: True, on success. ``bool``: True, on success.
Raises: Example:
RPCError: In case of a Telegram RPC error. .. code-block:: python
# Answer without alert
app.answer_callback_query(query_id, text=text)
# Answer with alert
app.answer_callback_query(query_id, text=text, show_alert=True)
""" """
return self.send( return self.send(
functions.messages.SetBotCallbackAnswer( functions.messages.SetBotCallbackAnswer(

View File

@ -29,28 +29,34 @@ class AnswerInlineQuery(BaseClient):
inline_query_id: str, inline_query_id: str,
results: List[InlineQueryResult], results: List[InlineQueryResult],
cache_time: int = 300, cache_time: int = 300,
is_personal: bool = None, is_gallery: bool = False,
is_personal: bool = False,
next_offset: str = "", next_offset: str = "",
switch_pm_text: str = "", switch_pm_text: str = "",
switch_pm_parameter: str = "" switch_pm_parameter: str = ""
): ):
"""Send answers to an inline query. """Send answers to an inline query.
No more than 50 results per query are allowed.
A maximum of 50 results per query is allowed.
Parameters: Parameters:
inline_query_id (``str``): inline_query_id (``str``):
Unique identifier for the answered query. Unique identifier for the answered query.
results (List of :obj:`InlineQueryResult <pyrogram.InlineQueryResult>`): results (List of :obj:`InlineQueryResult`):
A list of results for the inline query. A list of results for the inline query.
cache_time (``int``, *optional*): cache_time (``int``, *optional*):
The maximum amount of time in seconds that the result of the inline query may be cached on the server. The maximum amount of time in seconds that the result of the inline query may be cached on the server.
Defaults to 300. Defaults to 300.
is_gallery (``bool``, *optional*):
Pass True, if results should be displayed in gallery mode instead of list mode.
Defaults to False.
is_personal (``bool``, *optional*): is_personal (``bool``, *optional*):
Pass True, if results may be cached on the server side only for the user that sent the query. Pass True, if results may be cached on the server side only for the user that sent the query.
By default, results may be returned to any user who sends the same query. By default (False), results may be returned to any user who sends the same query.
next_offset (``str``, *optional*): next_offset (``str``, *optional*):
Pass the offset that a client should send in the next query with the same text to receive more results. Pass the offset that a client should send in the next query with the same text to receive more results.
@ -75,15 +81,24 @@ class AnswerInlineQuery(BaseClient):
Returns: Returns:
``bool``: True, on success. ``bool``: True, on success.
Raises: Example:
RPCError: In case of a Telegram RPC error. .. code-block:: python
from pyrogram import InlineQueryResultArticle, InputTextMessageContent
app.answer_inline_query(
inline_query_id,
results=[
InlineQueryResultArticle(
"Title",
InputTextMessageContent("Message content"))])
""" """
return self.send( return self.send(
functions.messages.SetInlineBotResults( functions.messages.SetInlineBotResults(
query_id=int(inline_query_id), query_id=int(inline_query_id),
results=[r.write() for r in results], results=[r.write() for r in results],
cache_time=cache_time, cache_time=cache_time,
gallery=None, gallery=is_gallery or None,
private=is_personal or None, private=is_personal or None,
next_offset=next_offset or None, next_offset=next_offset or None,
switch_pm=types.InlineBotSwitchPM( switch_pm=types.InlineBotSwitchPM(

View File

@ -51,8 +51,11 @@ class GetGameHighScores(BaseClient):
Returns: Returns:
List of :obj:`GameHighScore`: On success. List of :obj:`GameHighScore`: On success.
Raises: Example:
RPCError: In case of a Telegram RPC error. .. code-block:: python
scores = app.get_game_high_scores(user_id, chat_id, message_id)
print(scores)
""" """
# TODO: inline_message_id # TODO: inline_message_id

View File

@ -27,7 +27,7 @@ class GetInlineBotResults(BaseClient):
def get_inline_bot_results( def get_inline_bot_results(
self, self,
bot: Union[int, str], bot: Union[int, str],
query: str, query: str = "",
offset: str = "", offset: str = "",
latitude: float = None, latitude: float = None,
longitude: float = None longitude: float = None
@ -40,8 +40,9 @@ class GetInlineBotResults(BaseClient):
Unique identifier of the inline bot you want to get results from. You can specify Unique identifier of the inline bot you want to get results from. You can specify
a @username (str) or a bot ID (int). a @username (str) or a bot ID (int).
query (``str``): query (``str``, *optional*):
Text of the query (up to 512 characters). Text of the query (up to 512 characters).
Defaults to "" (empty string).
offset (``str``, *optional*): offset (``str``, *optional*):
Offset of the results to be returned. Offset of the results to be returned.
@ -58,8 +59,13 @@ class GetInlineBotResults(BaseClient):
:obj:`BotResults <pyrogram.api.types.messages.BotResults>`: On Success. :obj:`BotResults <pyrogram.api.types.messages.BotResults>`: On Success.
Raises: Raises:
RPCError: In case of a Telegram RPC error.
TimeoutError: In case the bot fails to answer within 10 seconds. TimeoutError: In case the bot fails to answer within 10 seconds.
Example:
.. code-block:: python
results = app.get_inline_bot_results("pyrogrambot")
print(results)
""" """
# TODO: Don't return the raw type # TODO: Don't return the raw type

View File

@ -53,8 +53,12 @@ class RequestCallbackAnswer(BaseClient):
or as an alert. or as an alert.
Raises: Raises:
RPCError: In case of a Telegram RPC error.
TimeoutError: In case the bot fails to answer within 10 seconds. TimeoutError: In case the bot fails to answer within 10 seconds.
Example:
.. code-block:: python
app.request_callback_answer(chat_id, message_id, "callback_data")
""" """
# Telegram only wants bytes, but we are allowed to pass strings too. # Telegram only wants bytes, but we are allowed to pass strings too.

View File

@ -62,8 +62,10 @@ class SendGame(BaseClient):
Returns: Returns:
:obj:`Message`: On success, the sent game message is returned. :obj:`Message`: On success, the sent game message is returned.
Raises: Example:
RPCError: In case of a Telegram RPC error. .. code-block:: python
app.send_game(chat_id, "gamename")
""" """
r = self.send( r = self.send(
functions.messages.SendMedia( functions.messages.SendMedia(

View File

@ -60,8 +60,10 @@ class SendInlineBotResult(BaseClient):
Returns: Returns:
:obj:`Message`: On success, the sent inline result message is returned. :obj:`Message`: On success, the sent inline result message is returned.
Raises: Example:
RPCError: In case of a Telegram RPC error. .. code-block:: python
app.send_inline_bot_result(chat_id, query_id, result_id)
""" """
return self.send( return self.send(
functions.messages.SendInlineBotResult( functions.messages.SendInlineBotResult(

View File

@ -66,8 +66,14 @@ class SetGameScore(BaseClient):
:obj:`Message` | ``bool``: On success, if the message was sent by the bot, the edited message is returned, :obj:`Message` | ``bool``: On success, if the message was sent by the bot, the edited message is returned,
True otherwise. True otherwise.
Raises: Example:
RPCError: In case of a Telegram RPC error. .. code-block:: python
# Set new score
app.set_game_score(user_id, 1000)
# Force set new score
app.set_game_score(user_id, 25, force=True)
""" """
r = self.send( r = self.send(
functions.messages.SetGameScore( functions.messages.SetGameScore(

View File

@ -16,8 +16,14 @@
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>. # along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
from .add_chat_members import AddChatMembers
from .archive_chats import ArchiveChats from .archive_chats import ArchiveChats
from .create_channel import CreateChannel
from .create_group import CreateGroup
from .create_supergroup import CreateSupergroup
from .delete_channel import DeleteChannel
from .delete_chat_photo import DeleteChatPhoto from .delete_chat_photo import DeleteChatPhoto
from .delete_supergroup import DeleteSupergroup
from .export_chat_invite_link import ExportChatInviteLink from .export_chat_invite_link import ExportChatInviteLink
from .get_chat import GetChat from .get_chat import GetChat
from .get_chat_member import GetChatMember from .get_chat_member import GetChatMember
@ -32,9 +38,9 @@ from .kick_chat_member import KickChatMember
from .leave_chat import LeaveChat from .leave_chat import LeaveChat
from .pin_chat_message import PinChatMessage from .pin_chat_message import PinChatMessage
from .promote_chat_member import PromoteChatMember from .promote_chat_member import PromoteChatMember
from .restrict_chat import RestrictChat
from .restrict_chat_member import RestrictChatMember from .restrict_chat_member import RestrictChatMember
from .set_chat_description import SetChatDescription from .set_chat_description import SetChatDescription
from .set_chat_permissions import SetChatPermissions
from .set_chat_photo import SetChatPhoto from .set_chat_photo import SetChatPhoto
from .set_chat_title import SetChatTitle from .set_chat_title import SetChatTitle
from .unarchive_chats import UnarchiveChats from .unarchive_chats import UnarchiveChats
@ -65,9 +71,15 @@ class Chats(
IterDialogs, IterDialogs,
IterChatMembers, IterChatMembers,
UpdateChatUsername, UpdateChatUsername,
RestrictChat, SetChatPermissions,
GetDialogsCount, GetDialogsCount,
ArchiveChats, ArchiveChats,
UnarchiveChats UnarchiveChats,
CreateGroup,
CreateSupergroup,
CreateChannel,
AddChatMembers,
DeleteChannel,
DeleteSupergroup
): ):
pass pass

View File

@ -0,0 +1,88 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2019 Dan Tès <https://github.com/delivrance>
#
# This file is part of Pyrogram.
#
# Pyrogram is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Pyrogram is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
from typing import Union, List
from pyrogram.api import functions, types
from ...ext import BaseClient
class AddChatMembers(BaseClient):
def add_chat_members(
self,
chat_id: Union[int, str],
user_ids: Union[Union[int, str], List[Union[int, str]]],
forward_limit: int = 100
) -> bool:
"""Add new chat members to a group, supergroup or channel
Parameters:
chat_id (``int`` | ``str``):
The group, supergroup or channel id
user_ids (``int`` | ``str`` | List of ``int`` or ``str``):
Users to add in the chat
You can pass an ID (int), username (str) or phone number (str).
Multiple users can be added by passing a list of IDs, usernames or phone numbers.
forward_limit (``int``, *optional*):
How many of the latest messages you want to forward to the new members. Pass 0 to forward none of them.
Only applicable to basic groups (the argument is ignored for supergroups or channels).
Defaults to 100 (max amount).
Returns:
``bool``: On success, True is returned.
Example:
.. code-block:: python
# Add one member to a group or channel
app.add_chat_members(chat_id, user_id)
# Add multiple members to a group or channel
app.add_chat_members(chat_id, [user_id1, user_id2, user_id3])
# Change forward_limit (for basic groups only)
app.add_chat_members(chat_id, user_id, forward_limit=25)
"""
peer = self.resolve_peer(chat_id)
if not isinstance(user_ids, list):
user_ids = [user_ids]
if isinstance(peer, types.InputPeerChat):
for user_id in user_ids:
self.send(
functions.messages.AddChatUser(
chat_id=peer.chat_id,
user_id=self.resolve_peer(user_id),
fwd_limit=forward_limit
)
)
else:
self.send(
functions.channels.InviteToChannel(
channel=peer,
users=[
self.resolve_peer(user_id)
for user_id in user_ids
]
)
)
return True

View File

@ -37,8 +37,14 @@ class ArchiveChats(BaseClient):
Returns: Returns:
``bool``: On success, True is returned. ``bool``: On success, True is returned.
Raises: Example:
RPCError: In case of a Telegram RPC error. .. code-block:: python
# Archive chat
app.archive_chats(chat_id)
# Archive multiple chats at once
app.archive_chats([chat_id1, chat_id2, chat_id3])
""" """
if not isinstance(chat_ids, list): if not isinstance(chat_ids, list):

View File

@ -0,0 +1,55 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2019 Dan Tès <https://github.com/delivrance>
#
# This file is part of Pyrogram.
#
# Pyrogram is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Pyrogram is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
import pyrogram
from pyrogram.api import functions
from ...ext import BaseClient
class CreateChannel(BaseClient):
def create_channel(
self,
title: str,
description: str = ""
) -> "pyrogram.Chat":
"""Create a new broadcast channel.
Parameters:
title (``title``):
The channel title.
description (``str``, *optional*):
The channel description.
Returns:
:obj:`Chat`: On success, a chat object is returned.
Example:
.. code-block:: python
app.create_channel("Channel Title", "Channel Description")
"""
r = self.send(
functions.channels.CreateChannel(
title=title,
about=description,
broadcast=True
)
)
return pyrogram.Chat._parse_chat(self, r.chats[0])

View File

@ -0,0 +1,65 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2019 Dan Tès <https://github.com/delivrance>
#
# This file is part of Pyrogram.
#
# Pyrogram is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Pyrogram is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
from typing import Union, List
import pyrogram
from pyrogram.api import functions
from ...ext import BaseClient
class CreateGroup(BaseClient):
def create_group(
self,
title: str,
users: Union[Union[int, str], List[Union[int, str]]]
) -> "pyrogram.Chat":
"""Create a new basic group.
.. note::
If you want to create a new supergroup, use :meth:`~pyrogram.Client.create_supergroup` instead.
Parameters:
title (``title``):
The group title.
users (``int`` | ``str`` | List of ``int`` or ``str``):
Users to create a chat with.
You must pass at least one user using their IDs (int), usernames (str) or phone numbers (str).
Multiple users can be invited by passing a list of IDs, usernames or phone numbers.
Returns:
:obj:`Chat`: On success, a chat object is returned.
Example:
.. code-block:: python
app.create_group("Group Title", user_id)
"""
if not isinstance(users, list):
users = [users]
r = self.send(
functions.messages.CreateChat(
title=title,
users=[self.resolve_peer(u) for u in users]
)
)
return pyrogram.Chat._parse_chat(self, r.chats[0])

View File

@ -0,0 +1,59 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2019 Dan Tès <https://github.com/delivrance>
#
# This file is part of Pyrogram.
#
# Pyrogram is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Pyrogram is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
import pyrogram
from pyrogram.api import functions
from ...ext import BaseClient
class CreateSupergroup(BaseClient):
def create_supergroup(
self,
title: str,
description: str = ""
) -> "pyrogram.Chat":
"""Create a new supergroup.
.. note::
If you want to create a new basic group, use :meth:`~pyrogram.Client.create_group` instead.
Parameters:
title (``title``):
The supergroup title.
description (``str``, *optional*):
The supergroup description.
Returns:
:obj:`Chat`: On success, a chat object is returned.
Example:
.. code-block:: python
app.create_supergroup("Supergroup Title", "Supergroup Description")
"""
r = self.send(
functions.channels.CreateChannel(
title=title,
about=description,
megagroup=True
)
)
return pyrogram.Chat._parse_chat(self, r.chats[0])

View File

@ -16,17 +16,33 @@
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>. # along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
from io import BytesIO
from ..tl_object import TLObject from typing import Union
from pyrogram.api import functions
from ...ext import BaseClient
class Null(TLObject): class DeleteChannel(BaseClient):
ID = 0x56730bcc def delete_channel(self, chat_id: Union[int, str]) -> bool:
"""Delete a channel.
@staticmethod Parameters:
def read(b: BytesIO, *args) -> None: chat_id (``int`` | ``str``):
return None The id of the channel to be deleted.
def __new__(cls) -> bytes: Returns:
return cls.ID.to_bytes(4, "little") ``bool``: On success, True is returned.
Example:
.. code-block:: python
app.delete_channel(channel_id)
"""
self.send(
functions.channels.DeleteChannel(
channel=self.resolve_peer(chat_id)
)
)
return True

View File

@ -28,12 +28,8 @@ class DeleteChatPhoto(BaseClient):
chat_id: Union[int, str] chat_id: Union[int, str]
) -> bool: ) -> bool:
"""Delete a chat photo. """Delete a chat photo.
Photos can't be changed for private chats.
You must be an administrator in the chat for this to work and must have the appropriate admin rights.
Note: You must be an administrator in the chat for this to work and must have the appropriate admin rights.
In regular groups (non-supergroups), this method will only work if the "All Members Are Admins"
setting is off.
Parameters: Parameters:
chat_id (``int`` | ``str``): chat_id (``int`` | ``str``):
@ -43,8 +39,12 @@ class DeleteChatPhoto(BaseClient):
``bool``: True on success. ``bool``: True on success.
Raises: Raises:
RPCError: In case of a Telegram RPC error. ValueError: if a chat_id belongs to user.
``ValueError`` if a chat_id belongs to user.
Example:
.. code-block:: python
app.delete_chat_photo(chat_id)
""" """
peer = self.resolve_peer(chat_id) peer = self.resolve_peer(chat_id)

View File

@ -0,0 +1,48 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2019 Dan Tès <https://github.com/delivrance>
#
# This file is part of Pyrogram.
#
# Pyrogram is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Pyrogram is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
from typing import Union
from pyrogram.api import functions
from ...ext import BaseClient
class DeleteSupergroup(BaseClient):
def delete_supergroup(self, chat_id: Union[int, str]) -> bool:
"""Delete a supergroup.
Parameters:
chat_id (``int`` | ``str``):
The id of the supergroup to be deleted.
Returns:
``bool``: On success, True is returned.
Example:
.. code-block:: python
app.delete_supergroup(supergroup_id)
"""
self.send(
functions.channels.DeleteChannel(
channel=self.resolve_peer(chat_id)
)
)
return True

View File

@ -47,22 +47,21 @@ class ExportChatInviteLink(BaseClient):
``str``: On success, the exported invite link is returned. ``str``: On success, the exported invite link is returned.
Raises: Raises:
RPCError: In case of a Telegram RPC error.
ValueError: In case the chat_id belongs to a user. ValueError: In case the chat_id belongs to a user.
Example:
.. code-block:: python
link = app.export_chat_invite_link(chat_id)
print(link)
""" """
peer = self.resolve_peer(chat_id) peer = self.resolve_peer(chat_id)
if isinstance(peer, types.InputPeerChat): if isinstance(peer, (types.InputPeerChat, types.InputPeerChannel)):
return self.send( return self.send(
functions.messages.ExportChatInvite( functions.messages.ExportChatInvite(
peer=peer peer=peer
) )
).link ).link
elif isinstance(peer, types.InputPeerChannel):
return self.send(
functions.channels.ExportInvite(
channel=peer
)
).link
else: else:
raise ValueError('The chat_id "{}" belongs to a user'.format(chat_id)) raise ValueError('The chat_id "{}" belongs to a user'.format(chat_id))

View File

@ -20,7 +20,7 @@ from typing import Union
import pyrogram import pyrogram
from pyrogram.api import functions, types from pyrogram.api import functions, types
from ...ext import BaseClient from ...ext import BaseClient, utils
class GetChat(BaseClient): class GetChat(BaseClient):
@ -44,8 +44,13 @@ class GetChat(BaseClient):
otherwise, a chat preview object is returned. otherwise, a chat preview object is returned.
Raises: Raises:
RPCError: In case of a Telegram RPC error.
ValueError: In case the chat invite link points to a chat you haven't joined yet. ValueError: In case the chat invite link points to a chat you haven't joined yet.
Example:
.. code-block:: python
chat = app.get_chat("pyrogram")
print(chat)
""" """
match = self.INVITE_LINK_RE.match(str(chat_id)) match = self.INVITE_LINK_RE.match(str(chat_id))
@ -65,7 +70,7 @@ class GetChat(BaseClient):
chat_id = -r.chat.id chat_id = -r.chat.id
if isinstance(r.chat, types.Channel): if isinstance(r.chat, types.Channel):
chat_id = int("-100" + str(r.chat.id)) chat_id = utils.get_channel_id(r.chat.id)
peer = self.resolve_peer(chat_id) peer = self.resolve_peer(chat_id)

View File

@ -44,8 +44,11 @@ class GetChatMember(BaseClient):
Returns: Returns:
:obj:`ChatMember`: On success, a chat member is returned. :obj:`ChatMember`: On success, a chat member is returned.
Raises: Example:
RPCError: In case of a Telegram RPC error. .. code-block:: python
dan = app.get_chat_member("pyrogramchat", "haskell")
print(dan)
""" """
chat = self.resolve_peer(chat_id) chat = self.resolve_peer(chat_id)
user = self.resolve_peer(user_id) user = self.resolve_peer(user_id)

View File

@ -91,8 +91,19 @@ class GetChatMembers(BaseClient):
List of :obj:`ChatMember`: On success, a list of chat members is returned. List of :obj:`ChatMember`: On success, a list of chat members is returned.
Raises: Raises:
RPCError: In case of a Telegram RPC error.
ValueError: In case you used an invalid filter or a chat id that belongs to a user. ValueError: In case you used an invalid filter or a chat id that belongs to a user.
Example:
.. code-block:: python
# Get first 200 recent members
app.get_chat_members("pyrogramchat")
# Get all administrators
app.get_chat_members("pyrogramchat", filter="administrators")
# Get all bots
app.get_chat_members("pyrogramchat", filter="bots")
""" """
peer = self.resolve_peer(chat_id) peer = self.resolve_peer(chat_id)

View File

@ -37,8 +37,13 @@ class GetChatMembersCount(BaseClient):
``int``: On success, the chat members count is returned. ``int``: On success, the chat members count is returned.
Raises: Raises:
RPCError: In case of a Telegram RPC error.
ValueError: In case a chat id belongs to user. ValueError: In case a chat id belongs to user.
Example:
.. code-block:: python
count = app.get_chat_members_count("pyrogramchat")
print(count)
""" """
peer = self.resolve_peer(chat_id) peer = self.resolve_peer(chat_id)

View File

@ -23,7 +23,7 @@ from typing import List
import pyrogram import pyrogram
from pyrogram.api import functions, types from pyrogram.api import functions, types
from pyrogram.errors import FloodWait from pyrogram.errors import FloodWait
from ...ext import BaseClient from ...ext import BaseClient, utils
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -56,8 +56,14 @@ class GetDialogs(BaseClient):
Returns: Returns:
List of :obj:`Dialog`: On success, a list of dialogs is returned. List of :obj:`Dialog`: On success, a list of dialogs is returned.
Raises: Example:
RPCError: In case of a Telegram RPC error. .. code-block:: python
# Get first 100 dialogs
app.get_dialogs()
# Get pinned dialogs
app.get_dialogs(pinned_only=True)
""" """
while True: while True:
@ -94,10 +100,8 @@ class GetDialogs(BaseClient):
chat_id = to_id.user_id chat_id = to_id.user_id
else: else:
chat_id = message.from_id chat_id = message.from_id
elif isinstance(to_id, types.PeerChat):
chat_id = -to_id.chat_id
else: else:
chat_id = int("-100" + str(to_id.channel_id)) chat_id = utils.get_peer_id(to_id)
messages[chat_id] = pyrogram.Message._parse(self, message, users, chats) messages[chat_id] = pyrogram.Message._parse(self, message, users, chats)

View File

@ -31,8 +31,11 @@ class GetDialogsCount(BaseClient):
Returns: Returns:
``int``: On success, the dialogs count is returned. ``int``: On success, the dialogs count is returned.
Raises: Example:
RPCError: In case of a Telegram RPC error. .. code-block:: python
count = app.get_dialogs_count()
print(count)
""" """
if pinned_only: if pinned_only:

View File

@ -77,8 +77,20 @@ class IterChatMembers(BaseClient):
Returns: Returns:
``Generator``: A generator yielding :obj:`ChatMember` objects. ``Generator``: A generator yielding :obj:`ChatMember` objects.
Raises: Example:
RPCError: In case of a Telegram RPC error. .. code-block:: python
# Iterate though all chat members
for member in app.iter_chat_members("pyrogramchat"):
print(member.user.first_name)
# Iterate though all administrators
for member in app.iter_chat_members("pyrogramchat", filter="administrators"):
print(member.user.first_name)
# Iterate though all bots
for member in app.iter_chat_members("pyrogramchat", filter="bots"):
print(member.user.first_name)
""" """
current = 0 current = 0
yielded = set() yielded = set()

View File

@ -46,8 +46,12 @@ class IterDialogs(BaseClient):
Returns: Returns:
``Generator``: A generator yielding :obj:`Dialog` objects. ``Generator``: A generator yielding :obj:`Dialog` objects.
Raises: Example:
RPCError: In case of a Telegram RPC error. .. code-block:: python
# Iterate through all dialogs
for dialog in app.iter_dialogs():
print(dialog.chat.first_name or dialog.chat.title)
""" """
current = 0 current = 0
total = limit or (1 << 31) - 1 total = limit or (1 << 31) - 1

View File

@ -36,8 +36,14 @@ class JoinChat(BaseClient):
Returns: Returns:
:obj:`Chat`: On success, a chat object is returned. :obj:`Chat`: On success, a chat object is returned.
Raises: Example:
RPCError: In case of a Telegram RPC error. .. code-block:: python
# Join chat via username
app.join_chat("pyrogram")
# Join chat via invite link
app.join_chat("https://t.me/joinchat/AAAAAE0QmSW3IUmm3UFR7A")
""" """
match = self.INVITE_LINK_RE.match(chat_id) match = self.INVITE_LINK_RE.match(chat_id)

View File

@ -57,8 +57,16 @@ class KickChatMember(BaseClient):
:obj:`Message` | ``bool``: On success, a service message will be returned (when applicable), otherwise, in :obj:`Message` | ``bool``: On success, a service message will be returned (when applicable), otherwise, in
case a message object couldn't be returned, True is returned. case a message object couldn't be returned, True is returned.
Raises: Example:
RPCError: In case of a Telegram RPC error. .. code-block:: python
from time import time
# Ban chat member forever
app.kick_chat_member(chat_id, user_id)
# Kick chat member and automatically unban after 24h
app.kick_chat_member(chat_id, user_id, int(time.time() + 86400))
""" """
chat_peer = self.resolve_peer(chat_id) chat_peer = self.resolve_peer(chat_id)
user_peer = self.resolve_peer(user_id) user_peer = self.resolve_peer(user_id)

View File

@ -37,9 +37,16 @@ class LeaveChat(BaseClient):
delete (``bool``, *optional*): delete (``bool``, *optional*):
Deletes the group chat dialog after leaving (for simple group chats, not supergroups). Deletes the group chat dialog after leaving (for simple group chats, not supergroups).
Defaults to False.
Raises: Example:
RPCError: In case of a Telegram RPC error. .. code-block:: python
# Leave chat or channel
app.leave_chat(chat_id)
# Leave basic chat and also delete the dialog
app.leave_chat(chat_id, delete=True)
""" """
peer = self.resolve_peer(chat_id) peer = self.resolve_peer(chat_id)

View File

@ -47,8 +47,14 @@ class PinChatMessage(BaseClient):
Returns: Returns:
``bool``: True on success. ``bool``: True on success.
Raises: Example:
RPCError: In case of a Telegram RPC error. .. code-block:: python
# Pin with notification
app.pin_chat_message(chat_id, message_id)
# Pin without notification
app.pin_chat_message(chat_id, message_id, disable_notification=True)
""" """
self.send( self.send(
functions.messages.UpdatePinnedMessage( functions.messages.UpdatePinnedMessage(

View File

@ -78,8 +78,11 @@ class PromoteChatMember(BaseClient):
Returns: Returns:
``bool``: True on success. ``bool``: True on success.
Raises: Example:
RPCError: In case of a Telegram RPC error. .. code-block:: python
# Promote chat member to supergroup admin
app.promote_chat_member(chat_id, user_id)
""" """
self.send( self.send(
functions.channels.EditAdmin( functions.channels.EditAdmin(

View File

@ -20,7 +20,7 @@ from typing import Union
from pyrogram.api import functions, types from pyrogram.api import functions, types
from ...ext import BaseClient from ...ext import BaseClient
from ...types.user_and_chats import Chat from ...types.user_and_chats import Chat, ChatPermissions
class RestrictChatMember(BaseClient): class RestrictChatMember(BaseClient):
@ -28,20 +28,13 @@ class RestrictChatMember(BaseClient):
self, self,
chat_id: Union[int, str], chat_id: Union[int, str],
user_id: Union[int, str], user_id: Union[int, str],
until_date: int = 0, permissions: ChatPermissions,
can_send_messages: bool = False, until_date: int = 0
can_send_media_messages: bool = False,
can_send_other_messages: bool = False,
can_add_web_page_previews: bool = False,
can_send_polls: bool = False,
can_change_info: bool = False,
can_invite_users: bool = False,
can_pin_messages: bool = False
) -> Chat: ) -> Chat:
"""Restrict a user in a supergroup. """Restrict a user in a supergroup.
The bot must be an administrator in the supergroup for this to work and must have the appropriate admin rights. You must be an administrator in the supergroup for this to work and must have the appropriate admin rights.
Pass True for all boolean parameters to lift restrictions from a user. Pass True for all permissions to lift restrictions from a user.
Parameters: Parameters:
chat_id (``int`` | ``str``): chat_id (``int`` | ``str``):
@ -51,42 +44,32 @@ class RestrictChatMember(BaseClient):
Unique identifier (int) or username (str) of the target user. Unique identifier (int) or username (str) of the target user.
For a contact that exists in your Telegram address book you can use his phone number (str). For a contact that exists in your Telegram address book you can use his phone number (str).
permissions (:obj:`ChatPermissions`):
New user permissions.
until_date (``int``, *optional*): until_date (``int``, *optional*):
Date when the user will be unbanned, unix time. Date when the user will be unbanned, unix time.
If user is banned for more than 366 days or less than 30 seconds from the current time they are If user is banned for more than 366 days or less than 30 seconds from the current time they are
considered to be banned forever. Defaults to 0 (ban forever). considered to be banned forever. Defaults to 0 (ban forever).
can_send_messages (``bool``, *optional*):
Pass True, if the user can send text messages, contacts, locations and venues.
can_send_media_messages (``bool``, *optional*):
Pass True, if the user can send audios, documents, photos, videos, video notes and voice notes,
implies can_send_messages.
can_send_other_messages (``bool``, *optional*):
Pass True, if the user can send animations, games, stickers and use inline bots,
implies can_send_messages.
can_add_web_page_previews (``bool``, *optional*):
Pass True, if the user may add web page previews to their messages, implies can_send_messages.
can_send_polls (``bool``, *optional*):
Pass True, if the user can send polls, implies can_send_messages.
can_change_info (``bool``, *optional*):
Pass True, if the user can change the chat title, photo and other settings.
can_invite_users (``bool``, *optional*):
Pass True, if the user can invite new users to the chat.
can_pin_messages (``bool``, *optional*):
Pass True, if the user can pin messages.
Returns: Returns:
:obj:`Chat`: On success, a chat object is returned. :obj:`Chat`: On success, a chat object is returned.
Raises: Example:
RPCError: In case of a Telegram RPC error. .. code-block:: python
from time import time
from pyrogram import ChatPermissions
# Completely restrict chat member (mute) forever
app.restrict_chat_member(chat_id, user_id, ChatPermissions())
# Chat member muted for 24h
app.restrict_chat_member(chat_id, user_id, ChatPermissions(), int(time.time() + 86400))
# Chat member can only send text messages
app.restrict_chat_member(chat_id, user_id, ChatPermissions(can_send_messages=True))
""" """
send_messages = True send_messages = True
send_media = True send_media = True
@ -100,35 +83,35 @@ class RestrictChatMember(BaseClient):
invite_users = True invite_users = True
pin_messages = True pin_messages = True
if can_send_messages: if permissions.can_send_messages:
send_messages = None send_messages = None
if can_send_media_messages: if permissions.can_send_media_messages:
send_messages = None send_messages = None
send_media = None send_media = None
if can_send_other_messages: if permissions.can_send_other_messages:
send_messages = None send_messages = None
send_stickers = None send_stickers = None
send_gifs = None send_gifs = None
send_games = None send_games = None
send_inline = None send_inline = None
if can_add_web_page_previews: if permissions.can_add_web_page_previews:
send_messages = None send_messages = None
embed_links = None embed_links = None
if can_send_polls: if permissions.can_send_polls:
send_messages = None send_messages = None
send_polls = None send_polls = None
if can_change_info: if permissions.can_change_info:
change_info = None change_info = None
if can_invite_users: if permissions.can_invite_users:
invite_users = None invite_users = None
if can_pin_messages: if permissions.can_pin_messages:
pin_messages = None pin_messages = None
r = self.send( r = self.send(

View File

@ -42,8 +42,12 @@ class SetChatDescription(BaseClient):
``bool``: True on success. ``bool``: True on success.
Raises: Raises:
RPCError: In case of a Telegram RPC error. ValueError: if a chat_id doesn't belong to a supergroup or a channel.
``ValueError`` if a chat_id doesn't belong to a supergroup or a channel.
Example:
.. code-block:: python
app.set_chat_description(chat_id, "New Description")
""" """
peer = self.resolve_peer(chat_id) peer = self.resolve_peer(chat_id)

View File

@ -20,60 +20,47 @@ from typing import Union
from pyrogram.api import functions, types from pyrogram.api import functions, types
from ...ext import BaseClient from ...ext import BaseClient
from ...types.user_and_chats import Chat from ...types.user_and_chats import Chat, ChatPermissions
class RestrictChat(BaseClient): class SetChatPermissions(BaseClient):
def restrict_chat( def set_chat_permissions(
self, self,
chat_id: Union[int, str], chat_id: Union[int, str],
can_send_messages: bool = False, permissions: ChatPermissions,
can_send_media_messages: bool = False,
can_send_other_messages: bool = False,
can_add_web_page_previews: bool = False,
can_send_polls: bool = False,
can_change_info: bool = False,
can_invite_users: bool = False,
can_pin_messages: bool = False
) -> Chat: ) -> Chat:
"""Restrict a chat. """Set default chat permissions for all members.
Pass True for all boolean parameters to lift restrictions from a chat.
You must be an administrator in the group or a supergroup for this to work and must have the
*can_restrict_members* admin rights.
Parameters: Parameters:
chat_id (``int`` | ``str``): chat_id (``int`` | ``str``):
Unique identifier (int) or username (str) of the target chat. Unique identifier (int) or username (str) of the target chat.
can_send_messages (``bool``, *optional*): permissions (:obj:`ChatPermissions`):
Pass True, if the user can send text messages, contacts, locations and venues. New default chat permissions.
can_send_media_messages (``bool``, *optional*):
Pass True, if the user can send audios, documents, photos, videos, video notes and voice notes,
implies can_send_messages.
can_send_other_messages (``bool``, *optional*):
Pass True, if the user can send animations, games, stickers and use inline bots,
implies can_send_messages.
can_add_web_page_previews (``bool``, *optional*):
Pass True, if the user may add web page previews to their messages, implies can_send_messages.
can_send_polls (``bool``, *optional*):
Pass True, if the user can send polls, implies can_send_messages.
can_change_info (``bool``, *optional*):
Pass True, if the user can change the chat title, photo and other settings.
can_invite_users (``bool``, *optional*):
Pass True, if the user can invite new users to the chat.
can_pin_messages (``bool``, *optional*):
Pass True, if the user can pin messages.
Returns: Returns:
:obj:`Chat`: On success, a chat object is returned. :obj:`Chat`: On success, a chat object is returned.
Raises: Example:
RPCError: In case of a Telegram RPC error. .. code-block:: python
from pyrogram import ChatPermissions
# Completely restrict chat
app.set_chat_permissions(chat_id, ChatPermissions())
# Chat members can only send text messages, media, stickers and GIFs
app.set_chat_permissions(
chat_id,
ChatPermissions(
can_send_messages=True,
can_send_media_messages=True,
can_send_other_messages=True
)
)
""" """
send_messages = True send_messages = True
send_media = True send_media = True
@ -87,35 +74,35 @@ class RestrictChat(BaseClient):
invite_users = True invite_users = True
pin_messages = True pin_messages = True
if can_send_messages: if permissions.can_send_messages:
send_messages = None send_messages = None
if can_send_media_messages: if permissions.can_send_media_messages:
send_messages = None send_messages = None
send_media = None send_media = None
if can_send_other_messages: if permissions.can_send_other_messages:
send_messages = None send_messages = None
send_stickers = None send_stickers = None
send_gifs = None send_gifs = None
send_games = None send_games = None
send_inline = None send_inline = None
if can_add_web_page_previews: if permissions.can_add_web_page_previews:
send_messages = None send_messages = None
embed_links = None embed_links = None
if can_send_polls: if permissions.can_send_polls:
send_messages = None send_messages = None
send_polls = None send_polls = None
if can_change_info: if permissions.can_change_info:
change_info = None change_info = None
if can_invite_users: if permissions.can_invite_users:
invite_users = None invite_users = None
if can_pin_messages: if permissions.can_pin_messages:
pin_messages = None pin_messages = None
r = self.send( r = self.send(

View File

@ -17,12 +17,11 @@
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>. # along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
import os import os
from base64 import b64decode
from struct import unpack from struct import unpack
from typing import Union from typing import Union
from pyrogram.api import functions, types from pyrogram.api import functions, types
from ...ext import BaseClient from ...ext import BaseClient, utils
class SetChatPhoto(BaseClient): class SetChatPhoto(BaseClient):
@ -32,38 +31,43 @@ class SetChatPhoto(BaseClient):
photo: str photo: str
) -> bool: ) -> bool:
"""Set a new profile photo for the chat. """Set a new profile photo for the chat.
Photos can't be changed for private chats.
You must be an administrator in the chat for this to work and must have the appropriate admin rights.
Note: You must be an administrator in the chat for this to work and must have the appropriate admin rights.
In regular groups (non-supergroups), this method will only work if the "All Members Are Admins"
setting is off.
Parameters: Parameters:
chat_id (``int`` | ``str``): chat_id (``int`` | ``str``):
Unique identifier (int) or username (str) of the target chat. Unique identifier (int) or username (str) of the target chat.
photo (``str``): photo (``str``):
New chat photo. You can pass a :obj:`Photo` id or a file path to upload a new photo. New chat photo. You can pass a :obj:`Photo` file_id or a file path to upload a new photo from your local
machine.
Returns: Returns:
``bool``: True on success. ``bool``: True on success.
Raises: Raises:
RPCError: In case of a Telegram RPC error.
ValueError: if a chat_id belongs to user. ValueError: if a chat_id belongs to user.
Example:
.. code-block:: python
# Set chat photo using a local file
app.set_chat_photo(chat_id, "photo.jpg")
# Set chat photo using an exiting Photo file_id
app.set_chat_photo(chat_id, photo.file_id)
""" """
peer = self.resolve_peer(chat_id) peer = self.resolve_peer(chat_id)
if os.path.exists(photo): if os.path.exists(photo):
photo = types.InputChatUploadedPhoto(file=self.save_file(photo)) photo = types.InputChatUploadedPhoto(file=self.save_file(photo))
else: else:
s = unpack("<qq", b64decode(photo + "=" * (-len(photo) % 4), "-_")) unpacked = unpack("<iiqqc", utils.decode(photo))
photo = types.InputChatPhoto( photo = types.InputChatPhoto(
id=types.InputPhoto( id=types.InputPhoto(
id=s[0], id=unpacked[2],
access_hash=s[1], access_hash=unpacked[3],
file_reference=b"" file_reference=b""
) )
) )

View File

@ -47,8 +47,12 @@ class SetChatTitle(BaseClient):
``bool``: True on success. ``bool``: True on success.
Raises: Raises:
RPCError: In case of a Telegram RPC error.
ValueError: In case a chat id belongs to user. ValueError: In case a chat id belongs to user.
Example:
.. code-block:: python
app.set_chat_title(chat_id, "New Title")
""" """
peer = self.resolve_peer(chat_id) peer = self.resolve_peer(chat_id)

View File

@ -37,8 +37,14 @@ class UnarchiveChats(BaseClient):
Returns: Returns:
``bool``: On success, True is returned. ``bool``: On success, True is returned.
Raises: Example:
RPCError: In case of a Telegram RPC error. .. code-block:: python
# Unarchive chat
app.unarchive_chats(chat_id)
# Unarchive multiple chats at once
app.unarchive_chats([chat_id1, chat_id2, chat_id3])
""" """
if not isinstance(chat_ids, list): if not isinstance(chat_ids, list):

View File

@ -43,8 +43,11 @@ class UnbanChatMember(BaseClient):
Returns: Returns:
``bool``: True on success. ``bool``: True on success.
Raises: Example:
RPCError: In case of a Telegram RPC error. .. code-block:: python
# Unban chat member right now
app.unban_chat_member(chat_id, user_id)
""" """
self.send( self.send(
functions.channels.EditBanned( functions.channels.EditBanned(

View File

@ -38,8 +38,10 @@ class UnpinChatMessage(BaseClient):
Returns: Returns:
``bool``: True on success. ``bool``: True on success.
Raises: Example:
RPCError: In case of a Telegram RPC error. .. code-block:: python
app.unpin_chat_message(chat_id)
""" """
self.send( self.send(
functions.messages.UpdatePinnedMessage( functions.messages.UpdatePinnedMessage(

View File

@ -42,8 +42,12 @@ class UpdateChatUsername(BaseClient):
``bool``: True on success. ``bool``: True on success.
Raises: Raises:
RPCError: In case of a Telegram RPC error.
ValueError: In case a chat id belongs to a user or chat. ValueError: In case a chat id belongs to a user or chat.
Example:
.. code-block:: python
app.update_chat_username(chat_id, "new_username")
""" """
peer = self.resolve_peer(chat_id) peer = self.resolve_peer(chat_id)

View File

@ -37,8 +37,15 @@ class AddContacts(BaseClient):
Returns: Returns:
:obj:`types.contacts.ImportedContacts` :obj:`types.contacts.ImportedContacts`
Raises: Example:
RPCError: In case of a Telegram RPC error. .. code-block:: python
from pyrogram import InputPhoneContact
app.add_contacts([
InputPhoneContact("39123456789", "Foo"),
InputPhoneContact("38987654321", "Bar"),
InputPhoneContact("01234567891", "Baz")])
""" """
imported_contacts = self.send( imported_contacts = self.send(
functions.contacts.ImportContacts( functions.contacts.ImportContacts(

View File

@ -38,8 +38,10 @@ class DeleteContacts(BaseClient):
Returns: Returns:
``bool``: True on success. ``bool``: True on success.
Raises: Example:
RPCError: In case of a Telegram RPC error. .. code-block:: python
app.delete_contacts([user_id1, user_id2, user_id3])
""" """
contacts = [] contacts = []

View File

@ -30,14 +30,16 @@ log = logging.getLogger(__name__)
class GetContacts(BaseClient): class GetContacts(BaseClient):
def get_contacts(self) -> List["pyrogram.User"]: def get_contacts(self) -> List["pyrogram.User"]:
# TODO: Create a Users object and return that
"""Get contacts from your Telegram address book. """Get contacts from your Telegram address book.
Returns: Returns:
List of :obj:`User`: On success, a list of users is returned. List of :obj:`User`: On success, a list of users is returned.
Raises: Example:
RPCError: In case of a Telegram RPC error. .. code-block:: python
contacts = app.get_contacts()
print(contacts)
""" """
while True: while True:
try: try:

View File

@ -27,8 +27,11 @@ class GetContactsCount(BaseClient):
Returns: Returns:
``int``: On success, the contacts count is returned. ``int``: On success, the contacts count is returned.
Raises: Example:
RPCError: In case of a Telegram RPC error. .. code-block:: python
count = app.get_contacts_count()
print(count)
""" """
return len(self.send(functions.contacts.GetContacts(hash=0)).contacts) return len(self.send(functions.contacts.GetContacts(hash=0)).contacts)

View File

@ -47,7 +47,7 @@ class OnCallbackQuery(BaseClient):
if isinstance(self, pyrogram.Client): if isinstance(self, pyrogram.Client):
self.add_handler(pyrogram.CallbackQueryHandler(func, filters), group) self.add_handler(pyrogram.CallbackQueryHandler(func, filters), group)
elif isinstance(self, Filter) or self is None: elif isinstance(self, Filter) or self is None:
func.pyrogram_plugin = ( func.handler = (
pyrogram.CallbackQueryHandler(func, self), pyrogram.CallbackQueryHandler(func, self),
group if filters is None else filters group if filters is None else filters
) )

View File

@ -47,7 +47,7 @@ class OnDeletedMessages(BaseClient):
if isinstance(self, pyrogram.Client): if isinstance(self, pyrogram.Client):
self.add_handler(pyrogram.DeletedMessagesHandler(func, filters), group) self.add_handler(pyrogram.DeletedMessagesHandler(func, filters), group)
elif isinstance(self, Filter) or self is None: elif isinstance(self, Filter) or self is None:
func.pyrogram_plugin = ( func.handler = (
pyrogram.DeletedMessagesHandler(func, self), pyrogram.DeletedMessagesHandler(func, self),
group if filters is None else filters group if filters is None else filters
) )

View File

@ -46,7 +46,7 @@ class OnInlineQuery(BaseClient):
if isinstance(self, pyrogram.Client): if isinstance(self, pyrogram.Client):
self.add_handler(pyrogram.InlineQueryHandler(func, filters), group) self.add_handler(pyrogram.InlineQueryHandler(func, filters), group)
elif isinstance(self, Filter) or self is None: elif isinstance(self, Filter) or self is None:
func.pyrogram_plugin = ( func.handler = (
pyrogram.InlineQueryHandler(func, self), pyrogram.InlineQueryHandler(func, self),
group if filters is None else filters group if filters is None else filters
) )

View File

@ -46,7 +46,7 @@ class OnMessage(BaseClient):
if isinstance(self, pyrogram.Client): if isinstance(self, pyrogram.Client):
self.add_handler(pyrogram.MessageHandler(func, filters), group) self.add_handler(pyrogram.MessageHandler(func, filters), group)
elif isinstance(self, Filter) or self is None: elif isinstance(self, Filter) or self is None:
func.pyrogram_plugin = ( func.handler = (
pyrogram.MessageHandler(func, self), pyrogram.MessageHandler(func, self),
group if filters is None else filters group if filters is None else filters
) )

View File

@ -46,7 +46,7 @@ class OnPoll(BaseClient):
if isinstance(self, pyrogram.Client): if isinstance(self, pyrogram.Client):
self.add_handler(pyrogram.PollHandler(func, filters), group) self.add_handler(pyrogram.PollHandler(func, filters), group)
elif isinstance(self, Filter) or self is None: elif isinstance(self, Filter) or self is None:
func.pyrogram_plugin = ( func.handler = (
pyrogram.PollHandler(func, self), pyrogram.PollHandler(func, self),
group if filters is None else filters group if filters is None else filters
) )

View File

@ -40,7 +40,7 @@ class OnRawUpdate(BaseClient):
if isinstance(self, pyrogram.Client): if isinstance(self, pyrogram.Client):
self.add_handler(pyrogram.RawUpdateHandler(func), group) self.add_handler(pyrogram.RawUpdateHandler(func), group)
else: else:
func.pyrogram_plugin = ( func.handler = (
pyrogram.RawUpdateHandler(func), pyrogram.RawUpdateHandler(func),
group if self is None else group group if self is None else group
) )

View File

@ -44,7 +44,7 @@ class OnUserStatus(BaseClient):
if isinstance(self, pyrogram.Client): if isinstance(self, pyrogram.Client):
self.add_handler(pyrogram.UserStatusHandler(func, filters), group) self.add_handler(pyrogram.UserStatusHandler(func, filters), group)
elif isinstance(self, Filter) or self is None: elif isinstance(self, Filter) or self is None:
func.pyrogram_plugin = ( func.handler = (
pyrogram.UserStatusHandler(func, self), pyrogram.UserStatusHandler(func, self),
group if filters is None else filters group if filters is None else filters
) )

View File

@ -33,7 +33,6 @@ from .get_messages import GetMessages
from .iter_history import IterHistory from .iter_history import IterHistory
from .read_history import ReadHistory from .read_history import ReadHistory
from .retract_vote import RetractVote from .retract_vote import RetractVote
from .send_animated_sticker import SendAnimatedSticker
from .send_animation import SendAnimation from .send_animation import SendAnimation
from .send_audio import SendAudio from .send_audio import SendAudio
from .send_cached_media import SendCachedMedia from .send_cached_media import SendCachedMedia
@ -85,7 +84,6 @@ class Messages(
IterHistory, IterHistory,
SendCachedMedia, SendCachedMedia,
GetHistoryCount, GetHistoryCount,
SendAnimatedSticker,
ReadHistory, ReadHistory,
EditInlineText, EditInlineText,
EditInlineCaption, EditInlineCaption,

View File

@ -50,8 +50,17 @@ class DeleteMessages(BaseClient):
Returns: Returns:
``bool``: True on success, False otherwise. ``bool``: True on success, False otherwise.
Raises: Example:
RPCError: In case of a Telegram RPC error. .. code-block:: python
# Delete one message
app.delete_messages(chat_id, message_id)
# Delete multiple messages at once
app.delete_messages(chat_id, list_of_message_ids)
# Delete messages only on your side (without revoking)
app.delete_messages(chat_id, message_id, revoke=False)
""" """
peer = self.resolve_peer(chat_id) peer = self.resolve_peer(chat_id)
message_ids = list(message_ids) if not isinstance(message_ids, int) else [message_ids] message_ids = list(message_ids) if not isinstance(message_ids, int) else [message_ids]

View File

@ -57,24 +57,23 @@ class DownloadMedia(BaseClient):
Blocks the code execution until the file has been downloaded. Blocks the code execution until the file has been downloaded.
Defaults to True. Defaults to True.
progress (``callable``): progress (``callable``, *optional*):
Pass a callback function to view the download progress. Pass a callback function to view the file transmission progress.
The function must take *(client, current, total, \*args)* as positional arguments (look at the section The function must take *(current, total)* as positional arguments (look at Other Parameters below for a
below for a detailed description). detailed description) and will be called back each time a new file chunk has been successfully
transmitted.
progress_args (``tuple``): progress_args (``tuple``, *optional*):
Extra custom arguments for the progress callback function. Useful, for example, if you want to pass Extra custom arguments for the progress callback function.
a chat_id and a message_id in order to edit a message with the updated progress. You can pass anything you need to be available in the progress callback scope; for example, a Message
object or a Client instance in order to edit the message with the updated progress status.
Other Parameters: Other Parameters:
client (:obj:`Client`):
The Client itself, useful when you want to call other API methods inside the callback function.
current (``int``): current (``int``):
The amount of bytes downloaded so far. The amount of bytes transmitted so far.
total (``int``): total (``int``):
The size of the file. The total size of the file.
*args (``tuple``, *optional*): *args (``tuple``, *optional*):
Extra custom arguments as defined in the *progress_args* parameter. Extra custom arguments as defined in the *progress_args* parameter.
@ -85,8 +84,16 @@ class DownloadMedia(BaseClient):
the download failed or was deliberately stopped with :meth:`~Client.stop_transmission`, None is returned. the download failed or was deliberately stopped with :meth:`~Client.stop_transmission`, None is returned.
Raises: Raises:
RPCError: In case of a Telegram RPC error.
ValueError: if the message doesn't contain any downloadable media ValueError: if the message doesn't contain any downloadable media
Example:
.. code-block:: python
# Download from Message
app.download_media(message)
# Download from file id
app.download_media("CAADBAADyg4AAvLQYAEYD4F7vcZ43AI")
""" """
error_message = "This message doesn't contain any downloadable media" error_message = "This message doesn't contain any downloadable media"
available_media = ("audio", "document", "photo", "sticker", "animation", "video", "voice", "video_note") available_media = ("audio", "document", "photo", "sticker", "animation", "video", "voice", "video_note")
@ -207,7 +214,8 @@ class DownloadMedia(BaseClient):
extension extension
) )
self.download_queue.put((data, directory, file_name, done, progress, progress_args, path)) # Cast to string because Path objects aren't supported by Python 3.5
self.download_queue.put((data, str(directory), str(file_name), done, progress, progress_args, path))
if block: if block:
done.wait() done.wait()

View File

@ -16,6 +16,8 @@
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>. # along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
from typing import Union
import pyrogram import pyrogram
from pyrogram.client.ext import BaseClient from pyrogram.client.ext import BaseClient
@ -25,10 +27,10 @@ class EditInlineCaption(BaseClient):
self, self,
inline_message_id: str, inline_message_id: str,
caption: str, caption: str,
parse_mode: str = "", parse_mode: Union[str, None] = object,
reply_markup: "pyrogram.InlineKeyboardMarkup" = None reply_markup: "pyrogram.InlineKeyboardMarkup" = None
) -> bool: ) -> bool:
"""Edit the caption of **inline** media messages. """Edit the caption of inline media messages.
Parameters: Parameters:
inline_message_id (``str``): inline_message_id (``str``):
@ -50,8 +52,11 @@ class EditInlineCaption(BaseClient):
Returns: Returns:
``bool``: On success, True is returned. ``bool``: On success, True is returned.
Raises: Example:
RPCError: In case of a Telegram RPC error. .. code-block:: python
# Bots only
app.edit_inline_caption(inline_message_id, "new media caption")
""" """
return self.edit_inline_text( return self.edit_inline_text(
inline_message_id=inline_message_id, inline_message_id=inline_message_id,

View File

@ -33,7 +33,7 @@ class EditInlineMedia(BaseClient):
media: InputMedia, media: InputMedia,
reply_markup: "pyrogram.InlineKeyboardMarkup" = None reply_markup: "pyrogram.InlineKeyboardMarkup" = None
) -> bool: ) -> bool:
"""Edit **inline** animation, audio, document, photo or video messages. """Edit inline animation, audio, document, photo or video messages.
When the inline message is edited, a new file can't be uploaded. Use a previously uploaded file via its file_id When the inline message is edited, a new file can't be uploaded. Use a previously uploaded file via its file_id
or specify a URL. or specify a URL.
@ -52,8 +52,21 @@ class EditInlineMedia(BaseClient):
Returns: Returns:
``bool``: On success, True is returned. ``bool``: On success, True is returned.
Raises: Example:
RPCError: In case of a Telegram RPC error. .. code-block:: python
from pyrogram import InputMediaPhoto, InputMediaVideo, InputMediaAudio
# Bots only
# Replace the current media with a local photo
app.edit_inline_media(inline_message_id, InputMediaPhoto("new_photo.jpg"))
# Replace the current media with a local video
app.edit_inline_media(inline_message_id, InputMediaVideo("new_video.mp4"))
# Replace the current media with a local audio
app.edit_inline_media(inline_message_id, InputMediaAudio("new_audio.mp3"))
""" """
caption = media.caption caption = media.caption
parse_mode = media.parse_mode parse_mode = media.parse_mode

View File

@ -27,7 +27,7 @@ class EditInlineReplyMarkup(BaseClient):
inline_message_id: str, inline_message_id: str,
reply_markup: "pyrogram.InlineKeyboardMarkup" = None reply_markup: "pyrogram.InlineKeyboardMarkup" = None
) -> bool: ) -> bool:
"""Edit only the reply markup of **inline** messages sent via the bot (for inline bots). """Edit only the reply markup of inline messages sent via the bot (for inline bots).
Parameters: Parameters:
inline_message_id (``str``): inline_message_id (``str``):
@ -39,8 +39,16 @@ class EditInlineReplyMarkup(BaseClient):
Returns: Returns:
``bool``: On success, True is returned. ``bool``: On success, True is returned.
Raises: Example:
RPCError: In case of a Telegram RPC error. .. code-block:: python
from pyrogram import InlineKeyboardMarkup, InlineKeyboardButton
# Bots only
app.edit_inline_reply_markup(
inline_message_id,
InlineKeyboardMarkup([[
InlineKeyboardButton("New button", callback_data="new_data")]]))
""" """
return self.send( return self.send(
functions.messages.EditInlineBotMessage( functions.messages.EditInlineBotMessage(

View File

@ -28,11 +28,11 @@ class EditInlineText(BaseClient):
self, self,
inline_message_id: str, inline_message_id: str,
text: str, text: str,
parse_mode: Union[str, None] = "", parse_mode: Union[str, None] = object,
disable_web_page_preview: bool = None, disable_web_page_preview: bool = None,
reply_markup: "pyrogram.InlineKeyboardMarkup" = None reply_markup: "pyrogram.InlineKeyboardMarkup" = None
) -> bool: ) -> bool:
"""Edit the text of **inline** messages. """Edit the text of inline messages.
Parameters: Parameters:
inline_message_id (``str``): inline_message_id (``str``):
@ -57,8 +57,18 @@ class EditInlineText(BaseClient):
Returns: Returns:
``bool``: On success, True is returned. ``bool``: On success, True is returned.
Raises: Example:
RPCError: In case of a Telegram RPC error. .. code-block:: python
# Bots only
# Simple edit text
app.edit_inline_text(inline_message_id, "new text")
# Take the same text message, remove the web page preview only
app.edit_inline_text(
inline_message_id, message.text,
disable_web_page_preview=True)
""" """
return self.send( return self.send(

View File

@ -28,7 +28,7 @@ class EditMessageCaption(BaseClient):
chat_id: Union[int, str], chat_id: Union[int, str],
message_id: int, message_id: int,
caption: str, caption: str,
parse_mode: str = "", parse_mode: Union[str, None] = object,
reply_markup: "pyrogram.InlineKeyboardMarkup" = None reply_markup: "pyrogram.InlineKeyboardMarkup" = None
) -> "pyrogram.Message": ) -> "pyrogram.Message":
"""Edit the caption of media messages. """Edit the caption of media messages.
@ -58,8 +58,10 @@ class EditMessageCaption(BaseClient):
Returns: Returns:
:obj:`Message`: On success, the edited message is returned. :obj:`Message`: On success, the edited message is returned.
Raises: Example:
RPCError: In case of a Telegram RPC error. .. code-block:: python
app.edit_message_caption(chat_id, message_id, "new media caption")
""" """
return self.edit_message_text( return self.edit_message_text(
chat_id=chat_id, chat_id=chat_id,

View File

@ -60,8 +60,19 @@ class EditMessageMedia(BaseClient):
Returns: Returns:
:obj:`Message`: On success, the edited message is returned. :obj:`Message`: On success, the edited message is returned.
Raises: Example:
RPCError: In case of a Telegram RPC error. .. code-block:: python
from pyrogram import InputMediaPhoto, InputMediaVideo, InputMediaAudio
# Replace the current media with a local photo
app.edit_message_media(chat_id, message_id, InputMediaPhoto("new_photo.jpg"))
# Replace the current media with a local video
app.edit_message_media(chat_id, message_id, InputMediaVideo("new_video.mp4"))
# Replace the current media with a local audio
app.edit_message_media(chat_id, message_id, InputMediaAudio("new_audio.mp3"))
""" """
caption = media.caption caption = media.caption
parse_mode = media.parse_mode parse_mode = media.parse_mode

View File

@ -47,8 +47,16 @@ class EditMessageReplyMarkup(BaseClient):
Returns: Returns:
:obj:`Message`: On success, the edited message is returned. :obj:`Message`: On success, the edited message is returned.
Raises: Example:
RPCError: In case of a Telegram RPC error. .. code-block:: python
from pyrogram import InlineKeyboardMarkup, InlineKeyboardButton
# Bots only
app.edit_message_reply_markup(
chat_id, message_id,
InlineKeyboardMarkup([[
InlineKeyboardButton("New button", callback_data="new_data")]]))
""" """
r = self.send( r = self.send(
functions.messages.EditMessage( functions.messages.EditMessage(

View File

@ -29,7 +29,7 @@ class EditMessageText(BaseClient):
chat_id: Union[int, str], chat_id: Union[int, str],
message_id: int, message_id: int,
text: str, text: str,
parse_mode: Union[str, None] = "", parse_mode: Union[str, None] = object,
disable_web_page_preview: bool = None, disable_web_page_preview: bool = None,
reply_markup: "pyrogram.InlineKeyboardMarkup" = None reply_markup: "pyrogram.InlineKeyboardMarkup" = None
) -> "pyrogram.Message": ) -> "pyrogram.Message":
@ -63,8 +63,16 @@ class EditMessageText(BaseClient):
Returns: Returns:
:obj:`Message`: On success, the edited message is returned. :obj:`Message`: On success, the edited message is returned.
Raises: Example:
RPCError: In case of a Telegram RPC error. .. code-block:: python
# Simple edit text
app.edit_message_text(chat_id, message_id, "new text")
# Take the same text message, remove the web page preview only
app.edit_message_text(
chat_id, message_id, message.text,
disable_web_page_preview=True)
""" """
r = self.send( r = self.send(

View File

@ -55,7 +55,8 @@ class ForwardMessages(BaseClient):
Users will receive a notification with no sound. Users will receive a notification with no sound.
as_copy (``bool``, *optional*): as_copy (``bool``, *optional*):
Pass True to forward messages without the forward header (i.e.: send a copy of the message content). Pass True to forward messages without the forward header (i.e.: send a copy of the message content so
that it appears as originally sent by you).
Defaults to False. Defaults to False.
remove_caption (``bool``, *optional*): remove_caption (``bool``, *optional*):
@ -68,8 +69,18 @@ class ForwardMessages(BaseClient):
is returned, otherwise, in case *message_ids* was an iterable, the returned value will be a list of is returned, otherwise, in case *message_ids* was an iterable, the returned value will be a list of
messages, even if such iterable contained just a single element. messages, even if such iterable contained just a single element.
Raises: Example:
RPCError: In case of a Telegram RPC error. .. code-block:: python
:emphasize-lines: 2,5,8
# Forward a single message
app.forward_messages("me", "pyrogram", 20)
# Forward multiple messages at once
app.forward_messages("me", "pyrogram", [3, 20, 27])
# Forward messages as copy
app.forward_messages("me", "pyrogram", 20, as_copy=True)
""" """
is_iterable = not isinstance(message_ids, int) is_iterable = not isinstance(message_ids, int)

View File

@ -70,8 +70,17 @@ class GetHistory(BaseClient):
Returns: Returns:
List of :obj:`Message` - On success, a list of the retrieved messages is returned. List of :obj:`Message` - On success, a list of the retrieved messages is returned.
Raises: Example:
RPCError: In case of a Telegram RPC error. .. code-block:: python
# Get the last 100 messages of a chat
app.get_history("pyrogramchat")
# Get the last 3 messages of a chat
app.get_history("pyrogramchat", limit=3)
# Get 3 messages after skipping the first 5
app.get_history("pyrogramchat", offset=5, limit=3)
""" """
offset_id = offset_id or (1 if reverse else 0) offset_id = offset_id or (1 if reverse else 0)

View File

@ -45,8 +45,10 @@ class GetHistoryCount(BaseClient):
Returns: Returns:
``int``: On success, the chat history count is returned. ``int``: On success, the chat history count is returned.
Raises: Example:
RPCError: In case of a Telegram RPC error. .. code-block:: python
app.get_history_count("pyrogramchat")
""" """
r = self.send( r = self.send(

View File

@ -28,6 +28,9 @@ from ...ext import BaseClient, utils
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
# TODO: Rewrite using a flag for replied messages and have message_ids non-optional
class GetMessages(BaseClient): class GetMessages(BaseClient):
def get_messages( def get_messages(
self, self,
@ -36,7 +39,8 @@ class GetMessages(BaseClient):
reply_to_message_ids: Union[int, Iterable[int]] = None, reply_to_message_ids: Union[int, Iterable[int]] = None,
replies: int = 1 replies: int = 1
) -> Union["pyrogram.Message", List["pyrogram.Message"]]: ) -> Union["pyrogram.Message", List["pyrogram.Message"]]:
"""Get one or more messages that belong to a specific chat. """Get one or more messages from a chat by using message identifiers.
You can retrieve up to 200 messages at once. You can retrieve up to 200 messages at once.
Parameters: Parameters:
@ -64,8 +68,26 @@ class GetMessages(BaseClient):
returned, otherwise, in case *message_ids* was an iterable, the returned value will be a list of messages, returned, otherwise, in case *message_ids* was an iterable, the returned value will be a list of messages,
even if such iterable contained just a single element. even if such iterable contained just a single element.
Example:
.. code-block:: python
# Get one message
app.get_messages("pyrogramchat", 51110)
# Get more than one message (list of messages)
app.get_messages("pyrogramchat", [44625, 51110])
# Get message by ignoring any replied-to message
app.get_messages(chat_id, message_id, replies=0)
# Get message with all chained replied-to messages
app.get_messages(chat_id, message_id, replies=-1)
# Get the replied-to message of a message
app.get_messages(chat_id, reply_to_message_ids=message_id)
Raises: Raises:
RPCError: In case of a Telegram RPC error. ValueError: In case of invalid arguments.
""" """
ids, ids_type = ( ids, ids_type = (
(message_ids, types.InputMessageID) if message_ids (message_ids, types.InputMessageID) if message_ids
@ -74,7 +96,7 @@ class GetMessages(BaseClient):
) )
if ids is None: if ids is None:
raise ValueError("No argument supplied") raise ValueError("No argument supplied. Either pass message_ids or reply_to_message_ids")
peer = self.resolve_peer(chat_id) peer = self.resolve_peer(chat_id)

View File

@ -64,8 +64,11 @@ class IterHistory(BaseClient):
Returns: Returns:
``Generator``: A generator yielding :obj:`Message` objects. ``Generator``: A generator yielding :obj:`Message` objects.
Raises: Example:
RPCError: In case of a Telegram RPC error. .. code-block:: python
for message in app.iter_history("pyrogram"):
print(message.text)
""" """
offset_id = offset_id or (1 if reverse else 0) offset_id = offset_id or (1 if reverse else 0)
current = 0 current = 0

View File

@ -43,8 +43,14 @@ class ReadHistory(BaseClient):
Returns: Returns:
``bool`` - On success, True is returned. ``bool`` - On success, True is returned.
Raises: Example:
RPCError: In case of a Telegram RPC error. .. code-block:: python
# Mark the whole chat as read
app.read_history("pyrogramlounge")
# Mark messages as read only up to the given message id
app.read_history("pyrogramlounge", 123456)
""" """
peer = self.resolve_peer(chat_id) peer = self.resolve_peer(chat_id)

View File

@ -43,8 +43,10 @@ class RetractVote(BaseClient):
Returns: Returns:
:obj:`Poll`: On success, the poll with the retracted vote is returned. :obj:`Poll`: On success, the poll with the retracted vote is returned.
Raises: Example:
RPCError: In case of a Telegram RPC error. .. code-block:: python
app.retract_vote(chat_id, message_id)
""" """
r = self.send( r = self.send(
functions.messages.SendVote( functions.messages.SendVote(

View File

@ -1,141 +0,0 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2019 Dan Tès <https://github.com/delivrance>
#
# This file is part of Pyrogram.
#
# Pyrogram is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Pyrogram is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
import os
from typing import Union
import pyrogram
from pyrogram.api import functions, types
from pyrogram.client.ext import BaseClient, utils
from pyrogram.errors import FilePartMissing
class SendAnimatedSticker(BaseClient):
def send_animated_sticker(
self,
chat_id: Union[int, str],
animated_sticker: str,
disable_notification: bool = None,
reply_to_message_id: int = None,
reply_markup: Union[
"pyrogram.InlineKeyboardMarkup",
"pyrogram.ReplyKeyboardMarkup",
"pyrogram.ReplyKeyboardRemove",
"pyrogram.ForceReply"
] = None,
progress: callable = None,
progress_args: tuple = ()
) -> Union["pyrogram.Message", None]:
"""Send .tgs animated stickers.
Parameters:
chat_id (``int`` | ``str``):
Unique identifier (int) or username (str) of the target chat.
For your personal cloud (Saved Messages) you can simply use "me" or "self".
For a contact that exists in your Telegram address book you can use his phone number (str).
animated_sticker (``str``):
Animated sticker to send.
Pass a file_id as string to send a animated sticker that exists on the Telegram servers,
pass an HTTP URL as a string for Telegram to get a .webp animated sticker file from the Internet, or
pass a file path as string to upload a new animated sticker that exists on your local machine.
disable_notification (``bool``, *optional*):
Sends the message silently.
Users will receive a notification with no sound.
reply_to_message_id (``int``, *optional*):
If the message is a reply, ID of the original message.
reply_markup (:obj:`InlineKeyboardMarkup` | :obj:`ReplyKeyboardMarkup` | :obj:`ReplyKeyboardRemove` | :obj:`ForceReply`, *optional*):
Additional interface options. An object for an inline keyboard, custom reply keyboard,
instructions to remove reply keyboard or to force a reply from the user.
progress (``callable``, *optional*):
Pass a callback function to view the upload progress.
The function must take *(client, current, total, \*args)* as positional arguments (look at the section
below for a detailed description).
progress_args (``tuple``, *optional*):
Extra custom arguments for the progress callback function. Useful, for example, if you want to pass
a chat_id and a message_id in order to edit a message with the updated progress.
Other Parameters:
client (:obj:`Client`):
The Client itself, useful when you want to call other API methods inside the callback function.
current (``int``):
The amount of bytes uploaded so far.
total (``int``):
The size of the file.
*args (``tuple``, *optional*):
Extra custom arguments as defined in the *progress_args* parameter.
You can either keep *\*args* or add every single extra argument in your function signature.
Returns:
:obj:`Message` | ``None``: On success, the sent animated sticker message is returned, otherwise, in case the
upload is deliberately stopped with :meth:`~Client.stop_transmission`, None is returned.
Raises:
RPCError: In case of a Telegram RPC error.
"""
file = None
try:
if os.path.exists(animated_sticker):
file = self.save_file(animated_sticker, progress=progress, progress_args=progress_args)
media = types.InputMediaUploadedDocument(
mime_type=self.guess_mime_type(animated_sticker) or "application/x-tgsticker",
file=file,
attributes=[
types.DocumentAttributeFilename(file_name=os.path.basename(animated_sticker))
]
)
elif animated_sticker.startswith("http"):
media = types.InputMediaDocumentExternal(
url=animated_sticker
)
else:
media = utils.get_input_media_from_file_id(animated_sticker, 5)
while True:
try:
r = self.send(
functions.messages.SendMedia(
peer=self.resolve_peer(chat_id),
media=media,
silent=disable_notification or None,
reply_to_msg_id=reply_to_message_id,
random_id=self.rnd_id(),
reply_markup=reply_markup.write() if reply_markup else None,
message=""
)
)
except FilePartMissing as e:
self.save_file(animated_sticker, file_id=file.id, file_part=e.x)
else:
for i in r.updates:
if isinstance(i, (types.UpdateNewMessage, types.UpdateNewChannelMessage)):
return pyrogram.Message._parse(
self, i.message,
{i.id: i for i in r.users},
{i.id: i for i in r.chats}
)
except BaseClient.StopTransmission:
return None

View File

@ -32,7 +32,7 @@ class SendAnimation(BaseClient):
animation: str, animation: str,
caption: str = "", caption: str = "",
unsave: bool = False, unsave: bool = False,
parse_mode: Union[str, None] = "", parse_mode: Union[str, None] = object,
duration: int = 0, duration: int = 0,
width: int = 0, width: int = 0,
height: int = 0, height: int = 0,
@ -66,7 +66,7 @@ class SendAnimation(BaseClient):
Animation caption, 0-1024 characters. Animation caption, 0-1024 characters.
unsave (``bool``, *optional*): unsave (``bool``, *optional*):
By default, the server will save into your own collection any new animation GIF you send. By default, the server will save into your own collection any new animation you send.
Pass True to automatically unsave the sent animation. Defaults to False. Pass True to automatically unsave the sent animation. Defaults to False.
parse_mode (``str``, *optional*): parse_mode (``str``, *optional*):
@ -103,23 +103,22 @@ class SendAnimation(BaseClient):
instructions to remove reply keyboard or to force a reply from the user. instructions to remove reply keyboard or to force a reply from the user.
progress (``callable``, *optional*): progress (``callable``, *optional*):
Pass a callback function to view the upload progress. Pass a callback function to view the file transmission progress.
The function must take *(client, current, total, \*args)* as positional arguments (look at the section The function must take *(current, total)* as positional arguments (look at Other Parameters below for a
below for a detailed description). detailed description) and will be called back each time a new file chunk has been successfully
transmitted.
progress_args (``tuple``, *optional*): progress_args (``tuple``, *optional*):
Extra custom arguments for the progress callback function. Useful, for example, if you want to pass Extra custom arguments for the progress callback function.
a chat_id and a message_id in order to edit a message with the updated progress. You can pass anything you need to be available in the progress callback scope; for example, a Message
object or a Client instance in order to edit the message with the updated progress status.
Other Parameters: Other Parameters:
client (:obj:`Client`):
The Client itself, useful when you want to call other API methods inside the callback function.
current (``int``): current (``int``):
The amount of bytes uploaded so far. The amount of bytes transmitted so far.
total (``int``): total (``int``):
The size of the file. The total size of the file.
*args (``tuple``, *optional*): *args (``tuple``, *optional*):
Extra custom arguments as defined in the *progress_args* parameter. Extra custom arguments as defined in the *progress_args* parameter.
@ -129,8 +128,23 @@ class SendAnimation(BaseClient):
:obj:`Message` | ``None``: On success, the sent animation message is returned, otherwise, in case the upload :obj:`Message` | ``None``: On success, the sent animation message is returned, otherwise, in case the upload
is deliberately stopped with :meth:`~Client.stop_transmission`, None is returned. is deliberately stopped with :meth:`~Client.stop_transmission`, None is returned.
Raises: Example:
RPCError: In case of a Telegram RPC error. .. code-block:: python
# Send animation by uploading from local file
app.send_animation("me", "animation.gif")
# Add caption to the animation
app.send_animation("me", "animation.gif", caption="cat")
# Unsave the animation once is sent
app.send_animation("me", "animation.gif", unsave=True)
# Keep track of the progress while uploading
def progress(current, total):
print("{:.1f}%".format(current * 100 / total))
app.send_animation("me", "animation.gif", progress=progress)
""" """
file = None file = None

View File

@ -31,7 +31,7 @@ class SendAudio(BaseClient):
chat_id: Union[int, str], chat_id: Union[int, str],
audio: str, audio: str,
caption: str = "", caption: str = "",
parse_mode: Union[str, None] = "", parse_mode: Union[str, None] = object,
duration: int = 0, duration: int = 0,
performer: str = None, performer: str = None,
title: str = None, title: str = None,
@ -100,23 +100,22 @@ class SendAudio(BaseClient):
instructions to remove reply keyboard or to force a reply from the user. instructions to remove reply keyboard or to force a reply from the user.
progress (``callable``, *optional*): progress (``callable``, *optional*):
Pass a callback function to view the upload progress. Pass a callback function to view the file transmission progress.
The function must take *(client, current, total, \*args)* as positional arguments (look at the section The function must take *(current, total)* as positional arguments (look at Other Parameters below for a
below for a detailed description). detailed description) and will be called back each time a new file chunk has been successfully
transmitted.
progress_args (``tuple``, *optional*): progress_args (``tuple``, *optional*):
Extra custom arguments for the progress callback function. Useful, for example, if you want to pass Extra custom arguments for the progress callback function.
a chat_id and a message_id in order to edit a message with the updated progress. You can pass anything you need to be available in the progress callback scope; for example, a Message
object or a Client instance in order to edit the message with the updated progress status.
Other Parameters: Other Parameters:
client (:obj:`Client`):
The Client itself, useful when you want to call other API methods inside the callback function.
current (``int``): current (``int``):
The amount of bytes uploaded so far. The amount of bytes transmitted so far.
total (``int``): total (``int``):
The size of the file. The total size of the file.
*args (``tuple``, *optional*): *args (``tuple``, *optional*):
Extra custom arguments as defined in the *progress_args* parameter. Extra custom arguments as defined in the *progress_args* parameter.
@ -126,8 +125,26 @@ class SendAudio(BaseClient):
:obj:`Message` | ``None``: On success, the sent audio message is returned, otherwise, in case the upload :obj:`Message` | ``None``: On success, the sent audio message is returned, otherwise, in case the upload
is deliberately stopped with :meth:`~Client.stop_transmission`, None is returned. is deliberately stopped with :meth:`~Client.stop_transmission`, None is returned.
Raises: Example:
RPCError: In case of a Telegram RPC error. .. code-block:: python
:emphasize-lines: 2,5,8-10,13-16
# Send audio file by uploading from file
app.send_audio("me", "audio.mp3")
# Add caption to the audio
app.send_audio("me", "audio.mp3", caption="shoegaze")
# Set audio metadata
app.send_audio(
"me", "audio.mp3",
title="Printemps émeraude", performer="Alcest", duration=440)
# Keep track of the progress while uploading
def progress(current, total):
print("{:.1f}%".format(current * 100 / total))
app.send_audio("me", "audio.mp3", progress=progress)
""" """
file = None file = None

View File

@ -29,7 +29,7 @@ class SendCachedMedia(BaseClient):
chat_id: Union[int, str], chat_id: Union[int, str],
file_id: str, file_id: str,
caption: str = "", caption: str = "",
parse_mode: Union[str, None] = "", parse_mode: Union[str, None] = object,
disable_notification: bool = None, disable_notification: bool = None,
reply_to_message_id: int = None, reply_to_message_id: int = None,
reply_markup: Union[ reply_markup: Union[
@ -79,8 +79,10 @@ class SendCachedMedia(BaseClient):
Returns: Returns:
:obj:`Message`: On success, the sent media message is returned. :obj:`Message`: On success, the sent media message is returned.
Raises: Example:
RPCError: In case of a Telegram RPC error. .. code-block:: python
app.send_cached_media("me", "CAADBAADyg4AAvLQYAEYD4F7vcZ43AI")
""" """
r = self.send( r = self.send(

View File

@ -64,8 +64,22 @@ class SendChatAction(BaseClient):
``bool``: On success, True is returned. ``bool``: On success, True is returned.
Raises: Raises:
RPCError: In case of a Telegram RPC error. ValueError: In case the provided string is not a valid chat action.
ValueError: In case the provided string is not a valid ChatAction.
Example:
.. code-block:: python
# Send "typing" chat action
app.send_chat_action(chat_id, "typing")
# Send "upload_video" chat action
app.send_chat_action(chat_id, "upload_video")
# Send "playing" chat action
app.send_chat_action(chat_id, "playing")
# Cancel any current chat action
app.send_chat_action(chat_id, "cancel")
""" """
try: try:

View File

@ -74,8 +74,10 @@ class SendContact(BaseClient):
Returns: Returns:
:obj:`Message`: On success, the sent contact message is returned. :obj:`Message`: On success, the sent contact message is returned.
Raises: Example:
RPCError: In case of a Telegram RPC error. .. code-block:: python
app.send_contact("me", "+39 123 456 7890", "Dan")
""" """
r = self.send( r = self.send(
functions.messages.SendMedia( functions.messages.SendMedia(

View File

@ -32,7 +32,7 @@ class SendDocument(BaseClient):
document: str, document: str,
thumb: str = None, thumb: str = None,
caption: str = "", caption: str = "",
parse_mode: Union[str, None] = "", parse_mode: Union[str, None] = object,
disable_notification: bool = None, disable_notification: bool = None,
reply_to_message_id: int = None, reply_to_message_id: int = None,
reply_markup: Union[ reply_markup: Union[
@ -86,23 +86,22 @@ class SendDocument(BaseClient):
instructions to remove reply keyboard or to force a reply from the user. instructions to remove reply keyboard or to force a reply from the user.
progress (``callable``, *optional*): progress (``callable``, *optional*):
Pass a callback function to view the upload progress. Pass a callback function to view the file transmission progress.
The function must take *(client, current, total, \*args)* as positional arguments (look at the section The function must take *(current, total)* as positional arguments (look at Other Parameters below for a
below for a detailed description). detailed description) and will be called back each time a new file chunk has been successfully
transmitted.
progress_args (``tuple``, *optional*): progress_args (``tuple``, *optional*):
Extra custom arguments for the progress callback function. Useful, for example, if you want to pass Extra custom arguments for the progress callback function.
a chat_id and a message_id in order to edit a message with the updated progress. You can pass anything you need to be available in the progress callback scope; for example, a Message
object or a Client instance in order to edit the message with the updated progress status.
Other Parameters: Other Parameters:
client (:obj:`Client`):
The Client itself, useful when you want to call other API methods inside the callback function.
current (``int``): current (``int``):
The amount of bytes uploaded so far. The amount of bytes transmitted so far.
total (``int``): total (``int``):
The size of the file. The total size of the file.
*args (``tuple``, *optional*): *args (``tuple``, *optional*):
Extra custom arguments as defined in the *progress_args* parameter. Extra custom arguments as defined in the *progress_args* parameter.
@ -112,8 +111,20 @@ class SendDocument(BaseClient):
:obj:`Message` | ``None``: On success, the sent document message is returned, otherwise, in case the upload :obj:`Message` | ``None``: On success, the sent document message is returned, otherwise, in case the upload
is deliberately stopped with :meth:`~Client.stop_transmission`, None is returned. is deliberately stopped with :meth:`~Client.stop_transmission`, None is returned.
Raises: Example:
RPCError: In case of a Telegram RPC error. .. code-block:: python
# Send document by uploading from local file
app.send_document("me", "document.zip")
# Add caption to the document file
app.send_document("me", "document.zip", caption="archive")
# Keep track of the progress while uploading
def progress(current, total):
print("{:.1f}%".format(current * 100 / total))
app.send_document("me", "document.zip", progress=progress)
""" """
file = None file = None

View File

@ -66,8 +66,10 @@ class SendLocation(BaseClient):
Returns: Returns:
:obj:`Message`: On success, the sent location message is returned. :obj:`Message`: On success, the sent location message is returned.
Raises: Example:
RPCError: In case of a Telegram RPC error. .. code-block:: python
app.send_location("me", 51.500729, -0.124583)
""" """
r = self.send( r = self.send(
functions.messages.SendMedia( functions.messages.SendMedia(

View File

@ -31,7 +31,6 @@ log = logging.getLogger(__name__)
class SendMediaGroup(BaseClient): class SendMediaGroup(BaseClient):
# TODO: Add progress parameter # TODO: Add progress parameter
# TODO: Figure out how to send albums using URLs
def send_media_group( def send_media_group(
self, self,
chat_id: Union[int, str], chat_id: Union[int, str],
@ -60,8 +59,19 @@ class SendMediaGroup(BaseClient):
Returns: Returns:
List of :obj:`Message`: On success, a list of the sent messages is returned. List of :obj:`Message`: On success, a list of the sent messages is returned.
Raises: Example:
RPCError: In case of a Telegram RPC error. .. code-block:: python
from pyrogram import InputMediaPhoto, InputMediaVideo
app.send_media_group(
"me",
[
InputMediaPhoto("photo1.jpg"),
InputMediaPhoto("photo2.jpg", caption="photo caption"),
InputMediaVideo("video.mp4", caption="a video")
]
)
""" """
multi_media = [] multi_media = []
@ -88,7 +98,24 @@ class SendMediaGroup(BaseClient):
id=types.InputPhoto( id=types.InputPhoto(
id=media.photo.id, id=media.photo.id,
access_hash=media.photo.access_hash, access_hash=media.photo.access_hash,
file_reference=b"" file_reference=media.photo.file_reference
)
)
elif i.media.startswith("http"):
media = self.send(
functions.messages.UploadMedia(
peer=self.resolve_peer(chat_id),
media=types.InputMediaPhotoExternal(
url=i.media
)
)
)
media = types.InputMediaPhoto(
id=types.InputPhoto(
id=media.photo.id,
access_hash=media.photo.access_hash,
file_reference=media.photo.file_reference
) )
) )
else: else:
@ -126,7 +153,24 @@ class SendMediaGroup(BaseClient):
id=types.InputDocument( id=types.InputDocument(
id=media.document.id, id=media.document.id,
access_hash=media.document.access_hash, access_hash=media.document.access_hash,
file_reference=b"" file_reference=media.document.file_reference
)
)
elif i.media.startswith("http"):
media = self.send(
functions.messages.UploadMedia(
peer=self.resolve_peer(chat_id),
media=types.InputMediaDocumentExternal(
url=i.media
)
)
)
media = types.InputMediaDocument(
id=types.InputDocument(
id=media.document.id,
access_hash=media.document.access_hash,
file_reference=media.document.file_reference
) )
) )
else: else:

View File

@ -28,7 +28,7 @@ class SendMessage(BaseClient):
self, self,
chat_id: Union[int, str], chat_id: Union[int, str],
text: str, text: str,
parse_mode: Union[str, None] = "", parse_mode: Union[str, None] = object,
disable_web_page_preview: bool = None, disable_web_page_preview: bool = None,
disable_notification: bool = None, disable_notification: bool = None,
reply_to_message_id: int = None, reply_to_message_id: int = None,
@ -74,9 +74,44 @@ class SendMessage(BaseClient):
Returns: Returns:
:obj:`Message`: On success, the sent text message is returned. :obj:`Message`: On success, the sent text message is returned.
Raises: Example:
RPCError: In case of a Telegram RPC error. .. code-block:: python
:emphasize-lines: 2,5,8,11,21-23,26-33
# Simple example
app.send_message("haskell", "Thanks for creating **Pyrogram**!")
# Disable web page previews
app.send_message("me", "https://docs.pyrogram.org", disable_web_page_preview=True)
# Reply to a message using its id
app.send_message("me", "this is a reply", reply_to_message_id=12345)
# Force HTML-only styles for this request only
app.send_message("me", "**not bold**, <i>italic<i>", parse_mode="html")
##
# For bots only, send messages with keyboards attached
##
from pyrogram import (
ReplyKeyboardMarkup, InlineKeyboardMarkup, InlineKeyboardButton)
# Send a normal keyboard
app.send_message(
chat_id, "Look at that button!",
reply_markup=ReplyKeyboardMarkup([["Nice!"]]))
# Send an inline keyboard
app.send_message(
chat_id, "These are inline buttons",
reply_markup=InlineKeyboardMarkup(
[
[InlineKeyboardButton("Data", callback_data="hidden_callback_data")],
[InlineKeyboardButton("Docs", url="https://docs.pyrogram.org")]
]))
""" """
message, entities = self.parser.parse(text, parse_mode).values() message, entities = self.parser.parse(text, parse_mode).values()
r = self.send( r = self.send(

View File

@ -31,7 +31,7 @@ class SendPhoto(BaseClient):
chat_id: Union[int, str], chat_id: Union[int, str],
photo: str, photo: str,
caption: str = "", caption: str = "",
parse_mode: Union[str, None] = "", parse_mode: Union[str, None] = object,
ttl_seconds: int = None, ttl_seconds: int = None,
disable_notification: bool = None, disable_notification: bool = None,
reply_to_message_id: int = None, reply_to_message_id: int = None,
@ -85,23 +85,22 @@ class SendPhoto(BaseClient):
instructions to remove reply keyboard or to force a reply from the user. instructions to remove reply keyboard or to force a reply from the user.
progress (``callable``, *optional*): progress (``callable``, *optional*):
Pass a callback function to view the upload progress. Pass a callback function to view the file transmission progress.
The function must take *(client, current, total, \*args)* as positional arguments (look at the section The function must take *(current, total)* as positional arguments (look at Other Parameters below for a
below for a detailed description). detailed description) and will be called back each time a new file chunk has been successfully
transmitted.
progress_args (``tuple``, *optional*): progress_args (``tuple``, *optional*):
Extra custom arguments for the progress callback function. Useful, for example, if you want to pass Extra custom arguments for the progress callback function.
a chat_id and a message_id in order to edit a message with the updated progress. You can pass anything you need to be available in the progress callback scope; for example, a Message
object or a Client instance in order to edit the message with the updated progress status.
Other Parameters: Other Parameters:
client (:obj:`Client`):
The Client itself, useful when you want to call other API methods inside the callback function.
current (``int``): current (``int``):
The amount of bytes uploaded so far. The amount of bytes transmitted so far.
total (``int``): total (``int``):
The size of the file. The total size of the file.
*args (``tuple``, *optional*): *args (``tuple``, *optional*):
Extra custom arguments as defined in the *progress_args* parameter. Extra custom arguments as defined in the *progress_args* parameter.
@ -111,8 +110,20 @@ class SendPhoto(BaseClient):
:obj:`Message` | ``None``: On success, the sent photo message is returned, otherwise, in case the upload :obj:`Message` | ``None``: On success, the sent photo message is returned, otherwise, in case the upload
is deliberately stopped with :meth:`~Client.stop_transmission`, None is returned. is deliberately stopped with :meth:`~Client.stop_transmission`, None is returned.
Raises: Example:
RPCError: In case of a Telegram RPC error. .. code-block:: python
# Send photo by uploading from local file
app.send_photo("me", "photo.jpg")
# Send photo by uploading from URL
app.send_photo("me", "https://i.imgur.com/BQBTP7d.png")
# Add caption to a photo
app.send_photo("me", "photo.jpg", caption="Holidays!")
# Send self-destructing photo
app.send_photo("me", "photo.jpg", ttl_seconds=10)
""" """
file = None file = None

Some files were not shown because too many files have changed in this diff Show More