diff --git a/pyrogram/client/ext/dispatcher.py b/pyrogram/client/ext/dispatcher.py index c931c8f7..55a31452 100644 --- a/pyrogram/client/ext/dispatcher.py +++ b/pyrogram/client/ext/dispatcher.py @@ -77,9 +77,7 @@ class Dispatcher: lambda upd, usr, cht: (pyrogram.CallbackQuery._parse(self.client, upd, usr), CallbackQueryHandler), (types.UpdateUserStatus,): - lambda upd, usr, cht: ( - pyrogram.UserStatus._parse(self.client, upd.status, upd.user_id), UserStatusHandler - ), + lambda upd, usr, cht: (pyrogram.User._parse_user_status(self.client, upd), UserStatusHandler), (types.UpdateBotInlineQuery,): lambda upd, usr, cht: (pyrogram.InlineQuery._parse(self.client, upd, usr), InlineQueryHandler), diff --git a/pyrogram/client/handlers/user_status_handler.py b/pyrogram/client/handlers/user_status_handler.py index 9b39aab6..1f84d77f 100644 --- a/pyrogram/client/handlers/user_status_handler.py +++ b/pyrogram/client/handlers/user_status_handler.py @@ -21,26 +21,24 @@ 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:`~Client.add_handler` + It is intended to be used with :meth:`~Client.add_handler`. - For a nicer way to register this handler, have a look at the - :meth:`~Client.on_user_status` decorator. + For a nicer way to register this handler, have a look at the :meth:`~Client.on_user_status` decorator. Parameters: callback (``callable``): - Pass a function that will be called when a new UserStatus update arrives. It takes *(client, user_status)* + Pass a function that will be called when a new user status update arrives. It takes *(client, user)* 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. + Pass one or more filters to allow only a subset of users 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. + user (:obj:`User`): + The user containing the updated status. """ def __init__(self, callback: callable, filters=None): diff --git a/pyrogram/client/types/user_and_chats/__init__.py b/pyrogram/client/types/user_and_chats/__init__.py index 922ac86a..29c45e09 100644 --- a/pyrogram/client/types/user_and_chats/__init__.py +++ b/pyrogram/client/types/user_and_chats/__init__.py @@ -23,8 +23,7 @@ from .chat_photo import ChatPhoto from .chat_preview import ChatPreview from .dialog import Dialog from .user import User -from .user_status import UserStatus __all__ = [ - "Chat", "ChatMember", "ChatPermissions", "ChatPhoto", "ChatPreview", "Dialog", "User", "UserStatus" + "Chat", "ChatMember", "ChatPermissions", "ChatPhoto", "ChatPreview", "Dialog", "User" ] diff --git a/pyrogram/client/types/user_and_chats/user.py b/pyrogram/client/types/user_and_chats/user.py index 75d8fa5f..43baca25 100644 --- a/pyrogram/client/types/user_and_chats/user.py +++ b/pyrogram/client/types/user_and_chats/user.py @@ -21,54 +21,68 @@ import html import pyrogram from pyrogram.api import types from .chat_photo import ChatPhoto -from .user_status import UserStatus from ..object import Object +from ..update import Update -class User(Object): +class User(Object, Update): """A Telegram user or bot. Parameters: id (``int``): Unique identifier for this user or bot. - is_self(``bool``): + is_self(``bool``, *optional*): True, if this user is you yourself. - is_contact(``bool``): + is_contact(``bool``, *optional*): True, if this user is in your contacts. - is_mutual_contact(``bool``): + is_mutual_contact(``bool``, *optional*): True, if you both have each other's contact. - is_deleted(``bool``): + is_deleted(``bool``, *optional*): True, if this user is deleted. - is_bot (``bool``): + is_bot (``bool``, *optional*): True, if this user is a bot. - is_verified (``bool``): + is_verified (``bool``, *optional*): True, if this user has been verified by Telegram. - is_restricted (``bool``): + is_restricted (``bool``, *optional*): True, if this user has been restricted. Bots only. See *restriction_reason* for details. - is_scam (``bool``): + is_scam (``bool``, *optional*): True, if this user has been flagged for scam. - is_support (``bool``): + is_support (``bool``, *optional*): True, if this user is part of the Telegram support team. - first_name (``str``): + first_name (``str``, *optional*): User's or bot's first name. - status (:obj:`UserStatus `, *optional*): - User's Last Seen status. Empty for bots. - last_name (``str``, *optional*): User's or bot's last name. + status (``str``, *optional*): + User's Last Seen & Online status. + Can be one of the following: + "*online*", user is online right now. + "*offline*", user is currently offline. + "*recently*", user with hidden last seen time who was online between 1 second and 2-3 days ago. + "*within_week*", user with hidden last seen time who was online between 2-3 and seven days ago. + "*within_month*", user with hidden last seen time who was online between 6-7 days and a month ago. + "*long_time_ago*", blocked user or user with hidden last seen time who was online more than a month ago. + *None*, for bots. + + last_online_date (``int``, *optional*): + Last online date of a user. Only available in case status is "*offline*". + + next_offline_date (``int``, *optional*): + Date when a user will automatically go offline. Only available in case status is "*online*". + username (``str``, *optional*): User's or bot's username. @@ -77,7 +91,7 @@ class User(Object): dc_id (``int``, *optional*): User's or bot's assigned DC (data center). Available only in case the user has set a public profile photo. - Note that this information is approximate: it is based on where Telegram stores a user profile pictures and + Note that this information is approximate; it is based on where Telegram stores a user profile pictures and does not by any means tell you the user location (i.e. a user might travel far away, but will still connect to its assigned DC). More info at `FAQs `_. @@ -94,8 +108,8 @@ class User(Object): __slots__ = [ "id", "is_self", "is_contact", "is_mutual_contact", "is_deleted", "is_bot", "is_verified", "is_restricted", - "is_scam", "is_support", "first_name", "last_name", "status", "username", "language_code", "dc_id", - "phone_number", "photo", "restriction_reason" + "is_scam", "is_support", "first_name", "last_name", "status", "last_online_date", "next_offline_date", + "username", "language_code", "dc_id", "phone_number", "photo", "restriction_reason" ] def __init__( @@ -103,18 +117,20 @@ class User(Object): *, client: "pyrogram.BaseClient" = None, id: int, - is_self: bool, - is_contact: bool, - is_mutual_contact: bool, - is_deleted: bool, - is_bot: bool, - is_verified: bool, - is_restricted: bool, - is_scam: bool, - is_support: bool, - first_name: str, + is_self: bool = None, + is_contact: bool = None, + is_mutual_contact: bool = None, + is_deleted: bool = None, + is_bot: bool = None, + is_verified: bool = None, + is_restricted: bool = None, + is_scam: bool = None, + is_support: bool = None, + first_name: str = None, last_name: str = None, - status: UserStatus = None, + status: str = None, + last_online_date: int = None, + next_offline_date: int = None, username: str = None, language_code: str = None, dc_id: int = None, @@ -137,6 +153,8 @@ class User(Object): self.first_name = first_name self.last_name = last_name self.status = status + self.last_online_date = last_online_date + self.next_offline_date = next_offline_date self.username = username self.language_code = language_code self.dc_id = dc_id @@ -168,7 +186,7 @@ class User(Object): is_support=user.support, first_name=user.first_name, last_name=user.last_name, - status=UserStatus._parse(client, user.status, user.id, user.bot), + **User._parse_status(user.status, user.bot), username=user.username, language_code=user.lang_code, dc_id=getattr(user.photo, "dc_id", None), @@ -178,6 +196,47 @@ class User(Object): client=client ) + @staticmethod + def _parse_status(user_status: types.UpdateUserStatus, is_bot: bool = False): + if isinstance(user_status, types.UserStatusOnline): + status, date = "online", user_status.expires + elif isinstance(user_status, types.UserStatusOffline): + status, date = "offline", user_status.was_online + elif isinstance(user_status, types.UserStatusRecently): + status, date = "recently", None + elif isinstance(user_status, types.UserStatusLastWeek): + status, date = "within_week", None + elif isinstance(user_status, types.UserStatusLastMonth): + status, date = "within_month", None + else: + status, date = "long_time_ago", None + + last_online_date = None + next_offline_date = None + + if is_bot: + status = None + + if status == "online": + next_offline_date = date + + if status == "offline": + last_online_date = date + + return { + "status": status, + "last_online_date": last_online_date, + "next_offline_date": next_offline_date + } + + @staticmethod + def _parse_user_status(client, user_status: types.UpdateUserStatus): + return User( + id=user_status.user_id, + **User._parse_status(user_status.status), + client=client + ) + def archive(self): """Bound method *archive* of :obj:`User`.