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/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/pyrogram/__init__.py b/pyrogram/__init__.py
index 6fb6fff4..12f181c3 100644
--- a/pyrogram/__init__.py
+++ b/pyrogram/__init__.py
@@ -29,12 +29,12 @@ from .api.errors import Error
from .client.types import (
Audio, Chat, ChatMember, ChatMembers, ChatPhoto, Contact, Document, InputMediaPhoto,
InputMediaVideo, InputMediaDocument, InputMediaAudio, InputMediaAnimation, InputPhoneContact,
- Location, Message, MessageEntity, Dialog, Dialogs, Photo, PhotoSize, Sticker, Update, User,
+ Location, Message, MessageEntity, Dialog, Dialogs, Photo, PhotoSize, Sticker, Update, User, UserStatus,
UserProfilePhotos, Venue, Animation, Video, VideoNote, Voice, CallbackQuery, Messages, ForceReply,
InlineKeyboardButton, InlineKeyboardMarkup, KeyboardButton, ReplyKeyboardMarkup, ReplyKeyboardRemove
)
from .client import (
Client, ChatAction, ParseMode, Emoji,
MessageHandler, DeletedMessagesHandler, CallbackQueryHandler,
- RawUpdateHandler, DisconnectHandler, Filters
+ RawUpdateHandler, DisconnectHandler, UserStatusHandler, Filters
)
diff --git a/pyrogram/client/__init__.py b/pyrogram/client/__init__.py
index b345de94..00b9905a 100644
--- a/pyrogram/client/__init__.py
+++ b/pyrogram/client/__init__.py
@@ -22,5 +22,5 @@ from .filters import Filters
from .handlers import (
MessageHandler, DeletedMessagesHandler,
CallbackQueryHandler, RawUpdateHandler,
- DisconnectHandler
+ DisconnectHandler, UserStatusHandler
)
diff --git a/pyrogram/client/dispatcher/dispatcher.py b/pyrogram/client/dispatcher/dispatcher.py
index 5506cbdd..a0c5e365 100644
--- a/pyrogram/client/dispatcher/dispatcher.py
+++ b/pyrogram/client/dispatcher/dispatcher.py
@@ -25,7 +25,7 @@ from threading import Thread
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
@@ -209,6 +216,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/utils.py b/pyrogram/client/ext/utils.py
index a497e3c9..c7a10db9 100644
--- a/pyrogram/client/ext/utils.py
+++ b/pyrogram/client/ext/utils.py
@@ -129,6 +129,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,
@@ -142,7 +166,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/handlers/__init__.py b/pyrogram/client/handlers/__init__.py
index d06b2a76..ff1ead7a 100644
--- a/pyrogram/client/handlers/__init__.py
+++ b/pyrogram/client/handlers/__init__.py
@@ -17,7 +17,8 @@
# along with Pyrogram. If not, see .
from .callback_query_handler import CallbackQueryHandler
+from .deleted_messages_handler import DeletedMessagesHandler
from .disconnect_handler import DisconnectHandler
from .message_handler import MessageHandler
-from .deleted_messages_handler import DeletedMessagesHandler
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 f84a922c..6cf9940a 100644
--- a/pyrogram/client/methods/decorators/__init__.py
+++ b/pyrogram/client/methods/decorators/__init__.py
@@ -17,11 +17,19 @@
# along with Pyrogram. If not, see .
from .on_callback_query import OnCallbackQuery
+from .on_deleted_messages import OnDeletedMessages
from .on_disconnect import OnDisconnect
from .on_message import OnMessage
-from .on_deleted_messages import OnDeletedMessages
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_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/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