diff --git a/compiler/api/compiler.py b/compiler/api/compiler.py
index bcb96ea4..cacd9342 100644
--- a/compiler/api/compiler.py
+++ b/compiler/api/compiler.py
@@ -506,6 +506,7 @@ def start():
f.write("\n 0xb0700028: \"pyrogram.client.types.Dialog\",")
f.write("\n 0xb0700029: \"pyrogram.client.types.Dialogs\",")
f.write("\n 0xb0700030: \"pyrogram.client.types.ChatMembers\",")
+ f.write("\n 0xb0700031: \"pyrogram.client.types.UserStatus\"")
f.write("\n}\n")
diff --git a/docs/source/index.rst b/docs/source/index.rst
index 6e740dd0..6210ff05 100644
--- a/docs/source/index.rst
+++ b/docs/source/index.rst
@@ -23,13 +23,13 @@ Welcome to Pyrogram
Community
-
+
-
-
@@ -49,14 +49,14 @@ Welcome to Pyrogram
app.run()
Welcome to Pyrogram's Documentation! Here you can find resources for learning how to use the library.
-Contents are organized by topic and can be accessed from the sidebar, or by following them one by one using the Next
-button at the end of each page. But first, here's a brief overview of what is this all about.
+Contents are organized into self-contained topics and can be accessed from the sidebar, or by following them in order
+using the Next button at the end of each page. But first, here's a brief overview of what is this all about.
About
-----
-**Pyrogram** is a brand new Telegram_ Client Library written from the ground up in Python and C. It can be used for building
-custom Telegram applications that interact with the MTProto API as both User and Bot.
+**Pyrogram** is a brand new Telegram_ Client Library written from the ground up in Python and C. It can be used for
+building custom Telegram applications that interact with the MTProto API as both User and Bot.
Features
--------
@@ -64,7 +64,7 @@ Features
- **Easy to use**: You can easily install Pyrogram using pip and start building your app right away.
- **High-level**: The low-level details of MTProto are abstracted and automatically handled.
- **Fast**: Crypto parts are boosted up by TgCrypto_, a high-performance library written in pure C.
-- **Updated** to the latest Telegram API version, currently Layer 81 on top of MTProto 2.0.
+- **Updated** to the latest Telegram API version, currently Layer 82 on top of MTProto 2.0.
- **Documented**: The Pyrogram API is well documented and resembles the Telegram Bot API.
- **Full API**, allowing to execute any advanced action an official client is able to do, and more.
@@ -84,6 +84,7 @@ To get started, press the Next button.
resources/UpdateHandling
resources/UsingFilters
+ resources/Plugins
resources/AutoAuthorization
resources/CustomizeSessions
resources/TgCrypto
diff --git a/docs/source/pyrogram/Client.rst b/docs/source/pyrogram/Client.rst
index 1cd1a072..50e213ea 100644
--- a/docs/source/pyrogram/Client.rst
+++ b/docs/source/pyrogram/Client.rst
@@ -30,6 +30,7 @@ Decorators
on_message
on_callback_query
on_deleted_messages
+ on_user_status
on_disconnect
on_raw_update
@@ -96,7 +97,8 @@ Users
get_me
get_users
get_user_profile_photos
- delete_profile_photos
+ set_user_profile_photo
+ delete_user_profile_photos
Contacts
--------
diff --git a/docs/source/pyrogram/Handlers.rst b/docs/source/pyrogram/Handlers.rst
index 9f69c2c9..3b748e9d 100644
--- a/docs/source/pyrogram/Handlers.rst
+++ b/docs/source/pyrogram/Handlers.rst
@@ -9,6 +9,7 @@ Handlers
MessageHandler
DeletedMessagesHandler
CallbackQueryHandler
+ UserStatusHandler
DisconnectHandler
RawUpdateHandler
@@ -21,6 +22,9 @@ Handlers
.. autoclass:: CallbackQueryHandler
:members:
+.. autoclass:: UserStatusHandler
+ :members:
+
.. autoclass:: DisconnectHandler
:members:
diff --git a/docs/source/pyrogram/Types.rst b/docs/source/pyrogram/Types.rst
index e8dc709c..8763c0e1 100644
--- a/docs/source/pyrogram/Types.rst
+++ b/docs/source/pyrogram/Types.rst
@@ -10,6 +10,7 @@ Users & Chats
:nosignatures:
User
+ UserStatus
Chat
ChatPhoto
ChatMember
@@ -73,6 +74,9 @@ Input Media
.. autoclass:: User
:members:
+.. autoclass:: UserStatus
+ :members:
+
.. autoclass:: Chat
:members:
diff --git a/docs/source/resources/Plugins.rst b/docs/source/resources/Plugins.rst
new file mode 100644
index 00000000..d490b37b
--- /dev/null
+++ b/docs/source/resources/Plugins.rst
@@ -0,0 +1,116 @@
+Plugins
+=======
+
+Pyrogram embeds an **automatic** and lightweight plugin system that is meant to greatly simplify the organization of
+large projects and to provide a way for creating pluggable components that can be **easily shared** across different
+Pyrogram applications with **minimal boilerplate code**.
+
+Introduction
+------------
+
+Prior to the plugin system, pluggable handlers were already possible. For instance, if you wanted to modularize your
+applications, you had to do something like this...
+
+ .. note:: This is an example application that replies in private chats with two messages: one containing the same
+ text message you sent and the other containing the reversed text message (e.g.: "pyrogram" -> "pyrogram" and
+ "margoryp"):
+
+ .. code-block:: text
+
+ myproject/
+ config.ini
+ handlers.py
+ main.py
+
+ - ``handlers.py``
+
+ .. code-block:: python
+
+ def echo(client, message):
+ message.reply(message.text)
+
+
+ def echo_reversed(client, message):
+ message.reply(message.text[::-1])
+
+ - ``main.py``
+
+ .. code-block:: python
+
+ from pyrogram import Client, MessageHandler, Filters
+
+ from handlers import echo, echo_reversed
+
+ app = Client("my_account")
+
+ app.add_handler(
+ MessageHandler(
+ echo,
+ Filters.text & Filters.private))
+
+ app.add_handler(
+ MessageHandler(
+ echo_reversed,
+ Filters.text & Filters.private),
+ group=1)
+
+ app.run()
+
+...which is already nice and doesn't add *too much* boilerplate code, but things can get boring still; you have to
+manually ``import``, manually :meth:`add_handler ` and manually instantiate each
+:obj:`MessageHandler ` object because **you can't use those cool decorators** for your
+functions. So... What if you could?
+
+Creating Plugins
+----------------
+
+Setting up your Pyrogram project to accommodate plugins is as easy as creating a folder and putting your files full of
+handlers inside.
+
+ .. note:: This is the same example application `as shown above <#introduction>`_, written using the plugin system.
+
+ .. code-block:: text
+ :emphasize-lines: 2, 3
+
+ myproject/
+ plugins/
+ handlers.py
+ config.ini
+ main.py
+
+ - ``plugins/handlers.py``
+
+ .. code-block:: python
+ :emphasize-lines: 4, 9
+
+ from pyrogram import Client, Filters
+
+
+ @Client.on_message(Filters.text & Filters.private)
+ def echo(client, message):
+ message.reply(message.text)
+
+
+ @Client.on_message(Filters.text & Filters.private, group=1)
+ def echo_reversed(client, message):
+ message.reply(message.text[::-1])
+
+ - ``main.py``
+
+ .. code-block:: python
+
+ from pyrogram import Client
+
+ Client("my_account").run()
+
+The first important thing to note is the ``plugins`` folder, whose name is default and can be changed easily by setting
+the ``plugins_dir`` parameter when creating a :obj:`Client `; you can put any python file in the
+plugins folder and each file can contain any decorated function (handlers). Your Pyrogram Client instance (in the
+``main.py`` file) will **automatically** scan the folder upon creation to search for valid handlers and register them
+for you.
+
+Then you'll notice you can now use decorators. That's right, you can apply the usual decorators to your callback
+functions in a static way, i.e. **without having the Client instance around**: simply use ``@Client`` (Client class)
+instead of the usual ``@app`` (Client instance) namespace and things will work just the same.
+
+The ``main.py`` script is now at its bare minimum and cleanest state.
diff --git a/docs/source/resources/UpdateHandling.rst b/docs/source/resources/UpdateHandling.rst
index 781a48af..12afe324 100644
--- a/docs/source/resources/UpdateHandling.rst
+++ b/docs/source/resources/UpdateHandling.rst
@@ -2,15 +2,15 @@ Update Handling
===============
Updates are events that happen in your Telegram account (incoming messages, new channel posts, new members join, ...)
-and can be handled by registering one or more callback functions in your app by using an `Handler <../pyrogram/Handlers.html>`_.
+and can be handled by registering one or more callback functions in your app by using `Handlers <../pyrogram/Handlers.html>`_.
To put it simply, whenever an update is received from Telegram it will be dispatched and your previously defined callback
-function(s) will be called back with the update itself as argument.
+function(s) matching it will be called back with the update itself as argument.
Registering an Handler
----------------------
-To explain how `Handlers <../pyrogram/Handlers.html>`_ work let's have a look at the most used one, the
+To explain how handlers work let's have a look at the most used one, the
:obj:`MessageHandler `, which will be in charge for handling :obj:`Message `
updates coming from all around your chats. Every other handler shares the same setup logic; you should not have troubles
settings them up once you learn from this section.
diff --git a/docs/source/start/Installation.rst b/docs/source/start/Installation.rst
index d3ddfe7d..41a7ccac 100644
--- a/docs/source/start/Installation.rst
+++ b/docs/source/start/Installation.rst
@@ -4,11 +4,12 @@ Installation
Being a Python library, Pyrogram requires Python to be installed in your system.
We recommend using the latest version of Python 3 and pip.
-Get Python 3 from https://www.python.org/downloads/ or with your package manager and pip
+Get Python 3 from https://www.python.org/downloads/ (or with your package manager) and pip
by following the instructions at https://pip.pypa.io/en/latest/installing/.
-.. note::
- Pyrogram supports Python 3 only, starting from version 3.4 and PyPy.
+.. important::
+
+ Pyrogram supports **Python 3** only, starting from version 3.4. **PyPy** is supported too.
Install Pyrogram
----------------
@@ -29,7 +30,7 @@ Bleeding Edge
-------------
If you want the latest development version of Pyrogram, you can install it straight from the develop_
-branch using this command:
+branch using this command (you might need to install **git** first):
.. code-block:: bash
diff --git a/docs/source/start/Setup.rst b/docs/source/start/Setup.rst
index e0cccc2c..24caa1f4 100644
--- a/docs/source/start/Setup.rst
+++ b/docs/source/start/Setup.rst
@@ -53,6 +53,7 @@ fits better for you:
)
.. note::
+
The examples below assume you have created a ``config.ini`` file, thus they won't show the *api_id*
and *api_hash* parameters usage.
@@ -74,7 +75,7 @@ the :class:`Client ` class by passing to it a ``session_name``
This starts an interactive shell asking you to input your **phone number** (including your `Country Code`_)
and the **phone code** you will receive:
-.. code::
+.. code-block:: text
Enter phone number: +39**********
Is "+39**********" correct? (y/n): y
@@ -84,7 +85,9 @@ After successfully authorizing yourself, a new file called ``my_account.session`
Pyrogram executing API calls with your identity. This file will be loaded again when you restart your app,
and as long as you keep the session alive, Pyrogram won't ask you again to enter your phone number.
-.. important:: Your ``*.session`` files are personal and must be kept secret.
+.. important::
+
+ Your ``*.session`` files are personal and must be kept secret.
Bot Authorization
-----------------
diff --git a/docs/source/start/Usage.rst b/docs/source/start/Usage.rst
index 6eac5cd1..6c1697b9 100644
--- a/docs/source/start/Usage.rst
+++ b/docs/source/start/Usage.rst
@@ -10,25 +10,38 @@ High-level API
The easiest and recommended way to interact with Telegram is via the high-level Pyrogram methods_ and types_, which are
named after the `Telegram Bot API`_.
-Examples (more on `GitHub `_):
-
-- Get information about the authorized user:
+Here's a simple example:
.. code-block:: python
+ from pyrogram import Client
+
+ app = Client("my_account")
+
+ app.start()
+
print(app.get_me())
+ app.send_message("me", "Hi there! I'm using **Pyrogram**")
+ app.send_location("me", 51.500729, -0.124583)
-- Send a message to yourself (Saved Messages):
+ app.stop()
+
+You can also use Pyrogram in a context manager with the ``with`` statement. The Client will automatically
+:meth:`start ` and :meth:`stop ` gracefully, even in case of unhandled
+exceptions in your code:
.. code-block:: python
- app.send_message("me", "Hi there! I'm using Pyrogram")
+ from pyrogram import Client
-- Upload a new photo (with caption):
+ app = Client("my_account")
- .. code-block:: python
+ with app:
+ print(app.get_me())
+ app.send_message("me", "Hi there! I'm using **Pyrogram**")
+ app.send_location("me", 51.500729, -0.124583)
- app.send_photo("me", "/home/dan/perla.jpg", "Cute!")
+More examples on `GitHub `_.
Raw Functions
-------------
@@ -38,7 +51,9 @@ you have to use the raw :mod:`functions ` and :mod:`type
``pyrogram.api`` package and call any Telegram API method you wish using the :meth:`send() `
method provided by the Client class.
-.. hint:: Every high-level method mentioned in the section above is built on top of these raw functions.
+.. hint::
+
+ Every high-level method mentioned in the section above is built on top of these raw functions.
Nothing stops you from using the raw functions only, but they are rather complex and `plenty of them`_ are already
re-implemented by providing a much simpler and cleaner interface which is very similar to the Bot API.
@@ -54,17 +69,13 @@ Examples (more on `GitHub 1 else "",
+ self.plugins_dir
+ ))
+
def save_session(self):
auth_key = base64.b64encode(self.auth_key).decode()
auth_key = [auth_key[i: i + 43] for i in range(0, len(auth_key), 43)]
diff --git a/pyrogram/client/dispatcher/dispatcher.py b/pyrogram/client/dispatcher/dispatcher.py
index d4c16e60..095b08ab 100644
--- a/pyrogram/client/dispatcher/dispatcher.py
+++ b/pyrogram/client/dispatcher/dispatcher.py
@@ -23,7 +23,7 @@ from collections import OrderedDict
import pyrogram
from pyrogram.api import types
from ..ext import utils
-from ..handlers import RawUpdateHandler, CallbackQueryHandler, MessageHandler, DeletedMessagesHandler
+from ..handlers import RawUpdateHandler, CallbackQueryHandler, MessageHandler, DeletedMessagesHandler, UserStatusHandler
log = logging.getLogger(__name__)
@@ -108,6 +108,8 @@ class Dispatcher:
callback_query = update.callback_query
+ user_status = update.user_status
+
if message and isinstance(handler, MessageHandler):
if not handler.check(message):
continue
@@ -123,6 +125,11 @@ class Dispatcher:
continue
args = (self.client, callback_query)
+ elif user_status and isinstance(handler, UserStatusHandler):
+ if not handler.check(user_status):
+ continue
+
+ args = (self.client, user_status)
else:
continue
@@ -207,6 +214,14 @@ class Dispatcher:
)
)
)
+ elif isinstance(update, types.UpdateUserStatus):
+ self.dispatch(
+ pyrogram.Update(
+ user_status=utils.parse_user_status(
+ update.status, update.user_id
+ )
+ )
+ )
else:
continue
except Exception as e:
diff --git a/pyrogram/client/ext/base_client.py b/pyrogram/client/ext/base_client.py
index c03feff0..ddb51ae0 100644
--- a/pyrogram/client/ext/base_client.py
+++ b/pyrogram/client/ext/base_client.py
@@ -48,6 +48,10 @@ class BaseClient:
UPDATES_WORKERS = 1
DOWNLOAD_WORKERS = 4
OFFLINE_SLEEP = 300
+ WORKERS = 4
+ WORKDIR = "."
+ CONFIG_FILE = "./config.ini"
+ PLUGINS_DIR = "./plugins"
MEDIA_TYPE_ID = {
0: "thumbnail",
diff --git a/pyrogram/client/ext/utils.py b/pyrogram/client/ext/utils.py
index f572040a..17f437dd 100644
--- a/pyrogram/client/ext/utils.py
+++ b/pyrogram/client/ext/utils.py
@@ -141,6 +141,30 @@ def parse_chat_photo(photo):
)
+def parse_user_status(user_status, user_id: int = None, is_bot: bool = False) -> pyrogram_types.UserStatus or None:
+ if is_bot:
+ return None
+
+ status = pyrogram_types.UserStatus(user_id)
+
+ if isinstance(user_status, types.UserStatusOnline):
+ status.online = True
+ status.date = user_status.expires
+ elif isinstance(user_status, types.UserStatusOffline):
+ status.offline = True
+ status.date = user_status.was_online
+ elif isinstance(user_status, types.UserStatusRecently):
+ status.recently = True
+ elif isinstance(user_status, types.UserStatusLastWeek):
+ status.within_week = True
+ elif isinstance(user_status, types.UserStatusLastMonth):
+ status.within_month = True
+ else:
+ status.long_time_ago = True
+
+ return status
+
+
def parse_user(user: types.User) -> pyrogram_types.User or None:
return pyrogram_types.User(
id=user.id,
@@ -154,7 +178,8 @@ def parse_user(user: types.User) -> pyrogram_types.User or None:
username=user.username,
language_code=user.lang_code,
phone_number=user.phone,
- photo=parse_chat_photo(user.photo)
+ photo=parse_chat_photo(user.photo),
+ status=parse_user_status(user.status, is_bot=user.bot),
) if user else None
diff --git a/pyrogram/client/filters/filters.py b/pyrogram/client/filters/filters.py
index 79ab0f5f..e80e230e 100644
--- a/pyrogram/client/filters/filters.py
+++ b/pyrogram/client/filters/filters.py
@@ -168,7 +168,7 @@ class Filters:
@staticmethod
def command(command: str or list,
- prefix: str = "/",
+ prefix: str or list = "/",
separator: str = " ",
case_sensitive: bool = False):
"""Filter commands, i.e.: text messages starting with "/" or any other custom prefix.
@@ -180,9 +180,9 @@ class Filters:
a command arrives, the command itself and its arguments will be stored in the *command*
field of the :class:`Message `.
- prefix (``str``, *optional*):
- The command prefix. Defaults to "/" (slash).
- Examples: /start, .help, !settings.
+ prefix (``str`` | ``list``, *optional*):
+ A prefix or a list of prefixes as string the filter should look for.
+ Defaults to "/" (slash). Examples: ".", "!", ["/", "!", "."].
separator (``str``, *optional*):
The command arguments separator. Defaults to " " (white space).
@@ -194,11 +194,14 @@ class Filters:
"""
def f(_, m):
- if m.text and m.text.startswith(_.p):
- t = m.text.split(_.s)
- c, a = t[0][len(_.p):], t[1:]
- c = c if _.cs else c.lower()
- m.command = ([c] + a) if c in _.c else None
+ if m.text:
+ for i in _.p:
+ if m.text.startswith(i):
+ t = m.text.split(_.s)
+ c, a = t[0][len(i):], t[1:]
+ c = c if _.cs else c.lower()
+ m.command = ([c] + a) if c in _.c else None
+ break
return bool(m.command)
@@ -211,7 +214,7 @@ class Filters:
else {c if case_sensitive
else c.lower()
for c in command},
- p=prefix,
+ p=set(prefix),
s=separator,
cs=case_sensitive
)
diff --git a/pyrogram/client/handlers/__init__.py b/pyrogram/client/handlers/__init__.py
index 0b5058e9..ff1ead7a 100644
--- a/pyrogram/client/handlers/__init__.py
+++ b/pyrogram/client/handlers/__init__.py
@@ -21,3 +21,4 @@ from .deleted_messages_handler import DeletedMessagesHandler
from .disconnect_handler import DisconnectHandler
from .message_handler import MessageHandler
from .raw_update_handler import RawUpdateHandler
+from .user_status_handler import UserStatusHandler
diff --git a/pyrogram/client/handlers/user_status_handler.py b/pyrogram/client/handlers/user_status_handler.py
new file mode 100644
index 00000000..2442d7eb
--- /dev/null
+++ b/pyrogram/client/handlers/user_status_handler.py
@@ -0,0 +1,54 @@
+# Pyrogram - Telegram MTProto API Client Library for Python
+# Copyright (C) 2017-2018 Dan Tès
+#
+# 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 .
+
+from .handler import Handler
+
+
+class UserStatusHandler(Handler):
+ """The UserStatus handler class. Used to handle user status updates (user going online or offline).
+ It is intended to be used with :meth:`add_handler() `
+
+ For a nicer way to register this handler, have a look at the
+ :meth:`on_user_status() ` decorator.
+
+ Args:
+ callback (``callable``):
+ Pass a function that will be called when a new UserStatus update arrives. It takes *(client, user_status)*
+ as positional arguments (look at the section below for a detailed description).
+
+ filters (:obj:`Filters `):
+ Pass one or more filters to allow only a subset of messages to be passed
+ in your callback function.
+
+ Other parameters:
+ client (:obj:`Client `):
+ The Client itself, useful when you want to call other API methods inside the user status handler.
+
+ user_status (:obj:`UserStatus `):
+ The received UserStatus update.
+ """
+
+ def __init__(self, callback: callable, filters=None):
+ super().__init__(callback, filters)
+
+ def check(self, user_status):
+ return (
+ self.filters(user_status)
+ if callable(self.filters)
+ else True
+ )
diff --git a/pyrogram/client/methods/decorators/__init__.py b/pyrogram/client/methods/decorators/__init__.py
index d45a9ee6..6cf9940a 100644
--- a/pyrogram/client/methods/decorators/__init__.py
+++ b/pyrogram/client/methods/decorators/__init__.py
@@ -21,7 +21,15 @@ from .on_deleted_messages import OnDeletedMessages
from .on_disconnect import OnDisconnect
from .on_message import OnMessage
from .on_raw_update import OnRawUpdate
+from .on_user_status import OnUserStatus
-class Decorators(OnMessage, OnDeletedMessages, OnCallbackQuery, OnRawUpdate, OnDisconnect):
+class Decorators(
+ OnMessage,
+ OnDeletedMessages,
+ OnCallbackQuery,
+ OnRawUpdate,
+ OnDisconnect,
+ OnUserStatus
+):
pass
diff --git a/pyrogram/client/methods/decorators/on_callback_query.py b/pyrogram/client/methods/decorators/on_callback_query.py
index 5f22fc92..8413515d 100644
--- a/pyrogram/client/methods/decorators/on_callback_query.py
+++ b/pyrogram/client/methods/decorators/on_callback_query.py
@@ -17,6 +17,7 @@
# along with Pyrogram. If not, see .
import pyrogram
+from pyrogram.client.filters.filter import Filter
from ...ext import BaseClient
@@ -36,7 +37,14 @@ class OnCallbackQuery(BaseClient):
"""
def decorator(func):
- self.add_handler(pyrogram.CallbackQueryHandler(func, filters), group)
- return func
+ handler = pyrogram.CallbackQueryHandler(func, filters)
+
+ if isinstance(self, Filter):
+ return pyrogram.CallbackQueryHandler(func, self), group if filters is None else filters
+
+ if self is not None:
+ self.add_handler(handler, group)
+
+ return handler, group
return decorator
diff --git a/pyrogram/client/methods/decorators/on_deleted_messages.py b/pyrogram/client/methods/decorators/on_deleted_messages.py
index 3f603c41..e4b2bc97 100644
--- a/pyrogram/client/methods/decorators/on_deleted_messages.py
+++ b/pyrogram/client/methods/decorators/on_deleted_messages.py
@@ -17,6 +17,7 @@
# along with Pyrogram. If not, see .
import pyrogram
+from pyrogram.client.filters.filter import Filter
from ...ext import BaseClient
@@ -36,7 +37,14 @@ class OnDeletedMessages(BaseClient):
"""
def decorator(func):
- self.add_handler(pyrogram.DeletedMessagesHandler(func, filters), group)
- return func
+ handler = pyrogram.DeletedMessagesHandler(func, filters)
+
+ if isinstance(self, Filter):
+ return pyrogram.DeletedMessagesHandler(func, self), group if filters is None else filters
+
+ if self is not None:
+ self.add_handler(handler, group)
+
+ return handler, group
return decorator
diff --git a/pyrogram/client/methods/decorators/on_disconnect.py b/pyrogram/client/methods/decorators/on_disconnect.py
index 4bc593e3..a639471b 100644
--- a/pyrogram/client/methods/decorators/on_disconnect.py
+++ b/pyrogram/client/methods/decorators/on_disconnect.py
@@ -28,7 +28,11 @@ class OnDisconnect(BaseClient):
"""
def decorator(func):
- self.add_handler(pyrogram.DisconnectHandler(func))
- return func
+ handler = pyrogram.DisconnectHandler(func)
+
+ if self is not None:
+ self.add_handler(handler)
+
+ return handler
return decorator
diff --git a/pyrogram/client/methods/decorators/on_message.py b/pyrogram/client/methods/decorators/on_message.py
index 0011e083..7a0d54a0 100644
--- a/pyrogram/client/methods/decorators/on_message.py
+++ b/pyrogram/client/methods/decorators/on_message.py
@@ -17,11 +17,12 @@
# along with Pyrogram. If not, see .
import pyrogram
+from pyrogram.client.filters.filter import Filter
from ...ext import BaseClient
class OnMessage(BaseClient):
- def on_message(self, filters=None, group: int = 0):
+ def on_message(self=None, filters=None, group: int = 0):
"""Use this decorator to automatically register a function for handling
messages. This does the same thing as :meth:`add_handler` using the
:class:`MessageHandler`.
@@ -36,7 +37,14 @@ class OnMessage(BaseClient):
"""
def decorator(func):
- self.add_handler(pyrogram.MessageHandler(func, filters), group)
- return func
+ handler = pyrogram.MessageHandler(func, filters)
+
+ if isinstance(self, Filter):
+ return pyrogram.MessageHandler(func, self), group if filters is None else filters
+
+ if self is not None:
+ self.add_handler(handler, group)
+
+ return handler, group
return decorator
diff --git a/pyrogram/client/methods/decorators/on_raw_update.py b/pyrogram/client/methods/decorators/on_raw_update.py
index 902d9854..7675a4f0 100644
--- a/pyrogram/client/methods/decorators/on_raw_update.py
+++ b/pyrogram/client/methods/decorators/on_raw_update.py
@@ -21,7 +21,7 @@ from ...ext import BaseClient
class OnRawUpdate(BaseClient):
- def on_raw_update(self, group: int = 0):
+ def on_raw_update(self=None, group: int = 0):
"""Use this decorator to automatically register a function for handling
raw updates. This does the same thing as :meth:`add_handler` using the
:class:`RawUpdateHandler`.
@@ -32,7 +32,14 @@ class OnRawUpdate(BaseClient):
"""
def decorator(func):
- self.add_handler(pyrogram.RawUpdateHandler(func), group)
- return func
+ handler = pyrogram.RawUpdateHandler(func)
+
+ if isinstance(self, int):
+ return handler, group if self is None else group
+
+ if self is not None:
+ self.add_handler(handler, group)
+
+ return handler, group
return decorator
diff --git a/pyrogram/client/methods/decorators/on_user_status.py b/pyrogram/client/methods/decorators/on_user_status.py
new file mode 100644
index 00000000..81367c3e
--- /dev/null
+++ b/pyrogram/client/methods/decorators/on_user_status.py
@@ -0,0 +1,41 @@
+# Pyrogram - Telegram MTProto API Client Library for Python
+# Copyright (C) 2017-2018 Dan Tès
+#
+# 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 .
+
+import pyrogram
+from ...ext import BaseClient
+
+
+class OnUserStatus(BaseClient):
+ def on_user_status(self, filters=None, group: int = 0):
+ """Use this decorator to automatically register a function for handling
+ user status updates. This does the same thing as :meth:`add_handler` using the
+ :class:`UserStatusHandler`.
+
+ Args:
+ filters (:obj:`Filters `):
+ Pass one or more filters to allow only a subset of UserStatus updated to be passed in your function.
+
+ group (``int``, *optional*):
+ The group identifier, defaults to 0.
+ """
+
+ def decorator(func):
+ self.add_handler(pyrogram.UserStatusHandler(func, filters), group)
+ return func
+
+ return decorator
diff --git a/pyrogram/client/methods/users/__init__.py b/pyrogram/client/methods/users/__init__.py
index f7c32b3b..11f51d19 100644
--- a/pyrogram/client/methods/users/__init__.py
+++ b/pyrogram/client/methods/users/__init__.py
@@ -16,15 +16,17 @@
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see .
-from .delete_profile_photos import DeleteProfilePhotos
+from .delete_user_profile_photos import DeleteUserProfilePhotos
from .get_me import GetMe
from .get_user_profile_photos import GetUserProfilePhotos
from .get_users import GetUsers
+from .set_user_profile_photo import SetUserProfilePhoto
class Users(
GetUserProfilePhotos,
- DeleteProfilePhotos,
+ SetUserProfilePhoto,
+ DeleteUserProfilePhotos,
GetUsers,
GetMe
):
diff --git a/pyrogram/client/methods/users/delete_profile_photos.py b/pyrogram/client/methods/users/delete_user_profile_photos.py
similarity index 94%
rename from pyrogram/client/methods/users/delete_profile_photos.py
rename to pyrogram/client/methods/users/delete_user_profile_photos.py
index cbbf3c20..1f403494 100644
--- a/pyrogram/client/methods/users/delete_profile_photos.py
+++ b/pyrogram/client/methods/users/delete_user_profile_photos.py
@@ -23,8 +23,8 @@ from pyrogram.api import functions, types
from ...ext import BaseClient
-class DeleteProfilePhotos(BaseClient):
- async def delete_profile_photos(self, id: str or list):
+class DeleteUserProfilePhotos(BaseClient):
+ async def delete_user_profile_photos(self, id: str or list):
"""Use this method to delete your own profile photos
Args:
diff --git a/pyrogram/client/methods/users/set_user_profile_photo.py b/pyrogram/client/methods/users/set_user_profile_photo.py
new file mode 100644
index 00000000..b107a968
--- /dev/null
+++ b/pyrogram/client/methods/users/set_user_profile_photo.py
@@ -0,0 +1,48 @@
+# Pyrogram - Telegram MTProto API Client Library for Python
+# Copyright (C) 2017-2018 Dan Tès
+#
+# 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 .
+
+from pyrogram.api import functions
+from ...ext import BaseClient
+
+
+class SetUserProfilePhoto(BaseClient):
+ def set_user_profile_photo(self, photo: str):
+ """Use this method to set a new profile photo.
+
+ This method only works for Users.
+ Bots profile photos must be set using BotFather.
+
+ Args:
+ photo (``str``):
+ Profile photo to set.
+ Pass a file path as string to upload a new photo that exists on your local machine.
+
+ Returns:
+ True on success.
+
+ Raises:
+ :class:`Error `
+ """
+
+ return bool(
+ self.send(
+ functions.photos.UploadProfilePhoto(
+ self.save_file(photo)
+ )
+ )
+ )
diff --git a/pyrogram/client/types/__init__.py b/pyrogram/client/types/__init__.py
index 230d5e5d..74c97ca1 100644
--- a/pyrogram/client/types/__init__.py
+++ b/pyrogram/client/types/__init__.py
@@ -36,5 +36,5 @@ from .messages_and_media import (
from .update import Update
from .user_and_chats import (
Chat, ChatMember, ChatMembers, ChatPhoto,
- Dialog, Dialogs, User
+ Dialog, Dialogs, User, UserStatus
)
diff --git a/pyrogram/client/types/update.py b/pyrogram/client/types/update.py
index c8959708..748108de 100644
--- a/pyrogram/client/types/update.py
+++ b/pyrogram/client/types/update.py
@@ -58,6 +58,9 @@ class Update(Object):
pre_checkout_query (:obj:`PreCheckoutQuery `, *optional*):
New incoming pre-checkout query. Contains full information about checkout.
+
+ user_status (:obj:`UserStatus `, *optional*):
+ User status (last seen date) update.
"""
ID = 0xb0700000
@@ -74,7 +77,8 @@ class Update(Object):
chosen_inline_result=None,
callback_query=None,
shipping_query=None,
- pre_checkout_query=None
+ pre_checkout_query=None,
+ user_status=None
):
self.message = message
self.edited_message = edited_message
@@ -87,3 +91,4 @@ class Update(Object):
self.callback_query = callback_query
self.shipping_query = shipping_query
self.pre_checkout_query = pre_checkout_query
+ self.user_status = user_status
diff --git a/pyrogram/client/types/user_and_chats/__init__.py b/pyrogram/client/types/user_and_chats/__init__.py
index 45915edc..f4742d83 100644
--- a/pyrogram/client/types/user_and_chats/__init__.py
+++ b/pyrogram/client/types/user_and_chats/__init__.py
@@ -22,4 +22,5 @@ from .chat_members import ChatMembers
from .chat_photo import ChatPhoto
from .dialog import Dialog
from .dialogs import Dialogs
+from .user_status import UserStatus
from .user import User
diff --git a/pyrogram/client/types/user_and_chats/user.py b/pyrogram/client/types/user_and_chats/user.py
index 9ae5dab2..9c7eec1f 100644
--- a/pyrogram/client/types/user_and_chats/user.py
+++ b/pyrogram/client/types/user_and_chats/user.py
@@ -41,6 +41,9 @@ class User(Object):
is_bot (``bool``):
True, if this user is a bot.
+ status (:obj:`UserStatus `):
+ User's Last Seen status. Empty for bots.
+
first_name (``str``):
User's or bot's first name.
@@ -70,6 +73,7 @@ class User(Object):
is_mutual_contact: bool,
is_deleted: bool,
is_bot: bool,
+ status,
first_name: str,
last_name: str = None,
username: str = None,
@@ -83,6 +87,7 @@ class User(Object):
self.is_mutual_contact = is_mutual_contact
self.is_deleted = is_deleted
self.is_bot = is_bot
+ self.status = status
self.first_name = first_name
self.last_name = last_name
self.username = username
diff --git a/pyrogram/client/types/user_and_chats/user_status.py b/pyrogram/client/types/user_and_chats/user_status.py
new file mode 100644
index 00000000..17b73ea1
--- /dev/null
+++ b/pyrogram/client/types/user_and_chats/user_status.py
@@ -0,0 +1,84 @@
+# Pyrogram - Telegram MTProto API Client Library for Python
+# Copyright (C) 2017-2018 Dan Tès
+#
+# 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 .
+
+from pyrogram.api.core import Object
+
+
+class UserStatus(Object):
+ """This object represents a User status (Last Seen privacy)
+
+ .. note::
+
+ You won't see exact last seen timestamps for people with whom you don't share your own. Instead, you get
+ "recently", "within_week", "within_month" or "long_time_ago" fields set.
+
+ Args:
+ user_id (``int``):
+ User's id. Only available for UserStatus updates.
+
+ online (``bool``):
+ True if the user is online in this moment, None otherwise.
+ If True, the "date" field will be also set containing the online expiration date (i.e.: the date when a
+ user will automatically go offline in case of no action by his client).
+
+ offline (``bool``):
+ True if the user is offline and has the Last Seen privacy setting visible for everybody, None otherwise.
+ If True, the "date" field will be also set containing the last seen date (i.e.: the date when a user
+ was online the last time).
+
+ date (``int``):
+ Exact date in unix time. Available only in case "online" or "offline" equals to True.
+
+ recently (``bool``):
+ True for users with hidden Last Seen privacy that have been online between 1 second and 2-3 days ago,
+ None otherwise.
+
+ within_week (``bool``):
+ True for users with hidden Last Seen privacy that have been online between 2-3 and seven days ago,
+ None otherwise.
+
+ within_month (``bool``):
+ True for users with hidden Last Seen privacy that have been online between 6-7 days and a month ago,
+ None otherwise.
+
+ long_time_ago (``bool``):
+ True for users with hidden Last Seen privacy that have been online more than a month ago (this is also
+ always shown to blocked users), None otherwise.
+ """
+
+ ID = 0xb0700031
+
+ def __init__(
+ self,
+ user_id: int = None,
+ online: bool = None,
+ offline: bool = None,
+ date: int = None,
+ recently: bool = None,
+ within_week: bool = None,
+ within_month: bool = None,
+ long_time_ago: bool = None
+ ):
+ self.user_id = user_id
+ self.online = online
+ self.offline = offline
+ self.date = date
+ self.recently = recently
+ self.within_week = within_week
+ self.within_month = within_month
+ self.long_time_ago = long_time_ago