Merge branch 'user-status' into develop

This commit is contained in:
Dan 2018-10-16 12:07:53 +02:00
commit 8a81bd1221
15 changed files with 254 additions and 10 deletions

View File

@ -506,6 +506,7 @@ def start():
f.write("\n 0xb0700028: \"pyrogram.client.types.Dialog\",") f.write("\n 0xb0700028: \"pyrogram.client.types.Dialog\",")
f.write("\n 0xb0700029: \"pyrogram.client.types.Dialogs\",") f.write("\n 0xb0700029: \"pyrogram.client.types.Dialogs\",")
f.write("\n 0xb0700030: \"pyrogram.client.types.ChatMembers\",") f.write("\n 0xb0700030: \"pyrogram.client.types.ChatMembers\",")
f.write("\n 0xb0700031: \"pyrogram.client.types.UserStatus\"")
f.write("\n}\n") f.write("\n}\n")

View File

@ -10,6 +10,7 @@ Users & Chats
:nosignatures: :nosignatures:
User User
UserStatus
Chat Chat
ChatPhoto ChatPhoto
ChatMember ChatMember
@ -73,6 +74,9 @@ Input Media
.. autoclass:: User .. autoclass:: User
:members: :members:
.. autoclass:: UserStatus
:members:
.. autoclass:: Chat .. autoclass:: Chat
:members: :members:

View File

@ -29,12 +29,12 @@ from .api.errors import Error
from .client.types import ( from .client.types import (
Audio, Chat, ChatMember, ChatMembers, ChatPhoto, Contact, Document, InputMediaPhoto, Audio, Chat, ChatMember, ChatMembers, ChatPhoto, Contact, Document, InputMediaPhoto,
InputMediaVideo, InputMediaDocument, InputMediaAudio, InputMediaAnimation, InputPhoneContact, 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, UserProfilePhotos, Venue, Animation, Video, VideoNote, Voice, CallbackQuery, Messages, ForceReply,
InlineKeyboardButton, InlineKeyboardMarkup, KeyboardButton, ReplyKeyboardMarkup, ReplyKeyboardRemove InlineKeyboardButton, InlineKeyboardMarkup, KeyboardButton, ReplyKeyboardMarkup, ReplyKeyboardRemove
) )
from .client import ( from .client import (
Client, ChatAction, ParseMode, Emoji, Client, ChatAction, ParseMode, Emoji,
MessageHandler, DeletedMessagesHandler, CallbackQueryHandler, MessageHandler, DeletedMessagesHandler, CallbackQueryHandler,
RawUpdateHandler, DisconnectHandler, Filters RawUpdateHandler, DisconnectHandler, UserStatusHandler, Filters
) )

View File

@ -22,5 +22,5 @@ from .filters import Filters
from .handlers import ( from .handlers import (
MessageHandler, DeletedMessagesHandler, MessageHandler, DeletedMessagesHandler,
CallbackQueryHandler, RawUpdateHandler, CallbackQueryHandler, RawUpdateHandler,
DisconnectHandler DisconnectHandler, UserStatusHandler
) )

View File

@ -25,7 +25,7 @@ from threading import Thread
import pyrogram import pyrogram
from pyrogram.api import types from pyrogram.api import types
from ..ext import utils from ..ext import utils
from ..handlers import RawUpdateHandler, CallbackQueryHandler, MessageHandler, DeletedMessagesHandler from ..handlers import RawUpdateHandler, CallbackQueryHandler, MessageHandler, DeletedMessagesHandler, UserStatusHandler
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -108,6 +108,8 @@ class Dispatcher:
callback_query = update.callback_query callback_query = update.callback_query
user_status = update.user_status
if message and isinstance(handler, MessageHandler): if message and isinstance(handler, MessageHandler):
if not handler.check(message): if not handler.check(message):
continue continue
@ -123,6 +125,11 @@ class Dispatcher:
continue continue
args = (self.client, callback_query) 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: else:
continue 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: else:
continue continue
except Exception as e: except Exception as e:

View File

@ -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: def parse_user(user: types.User) -> pyrogram_types.User or None:
return pyrogram_types.User( return pyrogram_types.User(
id=user.id, id=user.id,
@ -142,7 +166,8 @@ def parse_user(user: types.User) -> pyrogram_types.User or None:
username=user.username, username=user.username,
language_code=user.lang_code, language_code=user.lang_code,
phone_number=user.phone, 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 ) if user else None

View File

@ -17,7 +17,8 @@
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>. # along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
from .callback_query_handler import CallbackQueryHandler from .callback_query_handler import CallbackQueryHandler
from .deleted_messages_handler import DeletedMessagesHandler
from .disconnect_handler import DisconnectHandler from .disconnect_handler import DisconnectHandler
from .message_handler import MessageHandler from .message_handler import MessageHandler
from .deleted_messages_handler import DeletedMessagesHandler
from .raw_update_handler import RawUpdateHandler from .raw_update_handler import RawUpdateHandler
from .user_status_handler import UserStatusHandler

View File

@ -0,0 +1,54 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2018 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 .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() <pyrogram.Client.add_handler>`
For a nicer way to register this handler, have a look at the
:meth:`on_user_status() <pyrogram.Client.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 <pyrogram.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 <pyrogram.Client>`):
The Client itself, useful when you want to call other API methods inside the user status handler.
user_status (:obj:`UserStatus <pyrogram.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
)

View File

@ -17,11 +17,19 @@
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>. # along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
from .on_callback_query import OnCallbackQuery from .on_callback_query import OnCallbackQuery
from .on_deleted_messages import OnDeletedMessages
from .on_disconnect import OnDisconnect from .on_disconnect import OnDisconnect
from .on_message import OnMessage from .on_message import OnMessage
from .on_deleted_messages import OnDeletedMessages
from .on_raw_update import OnRawUpdate 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 pass

View File

@ -0,0 +1,41 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2018 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 ...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 <pyrogram.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

View File

@ -36,5 +36,5 @@ from .messages_and_media import (
from .update import Update from .update import Update
from .user_and_chats import ( from .user_and_chats import (
Chat, ChatMember, ChatMembers, ChatPhoto, Chat, ChatMember, ChatMembers, ChatPhoto,
Dialog, Dialogs, User Dialog, Dialogs, User, UserStatus
) )

View File

@ -58,6 +58,9 @@ class Update(Object):
pre_checkout_query (:obj:`PreCheckoutQuery <pyrogram.PreCheckoutQuery>`, *optional*): pre_checkout_query (:obj:`PreCheckoutQuery <pyrogram.PreCheckoutQuery>`, *optional*):
New incoming pre-checkout query. Contains full information about checkout. New incoming pre-checkout query. Contains full information about checkout.
user_status (:obj:`UserStatus <pyrogram.UserStatus>`, *optional*):
User status (last seen date) update.
""" """
ID = 0xb0700000 ID = 0xb0700000
@ -74,7 +77,8 @@ class Update(Object):
chosen_inline_result=None, chosen_inline_result=None,
callback_query=None, callback_query=None,
shipping_query=None, shipping_query=None,
pre_checkout_query=None pre_checkout_query=None,
user_status=None
): ):
self.message = message self.message = message
self.edited_message = edited_message self.edited_message = edited_message
@ -87,3 +91,4 @@ class Update(Object):
self.callback_query = callback_query self.callback_query = callback_query
self.shipping_query = shipping_query self.shipping_query = shipping_query
self.pre_checkout_query = pre_checkout_query self.pre_checkout_query = pre_checkout_query
self.user_status = user_status

View File

@ -22,4 +22,5 @@ from .chat_members import ChatMembers
from .chat_photo import ChatPhoto from .chat_photo import ChatPhoto
from .dialog import Dialog from .dialog import Dialog
from .dialogs import Dialogs from .dialogs import Dialogs
from .user_status import UserStatus
from .user import User from .user import User

View File

@ -41,6 +41,9 @@ class User(Object):
is_bot (``bool``): is_bot (``bool``):
True, if this user is a bot. True, if this user is a bot.
status (:obj:`UserStatus <pyrogram.UserStatus>`):
User's Last Seen status. Empty for bots.
first_name (``str``): first_name (``str``):
User's or bot's first name. User's or bot's first name.
@ -70,6 +73,7 @@ class User(Object):
is_mutual_contact: bool, is_mutual_contact: bool,
is_deleted: bool, is_deleted: bool,
is_bot: bool, is_bot: bool,
status,
first_name: str, first_name: str,
last_name: str = None, last_name: str = None,
username: str = None, username: str = None,
@ -83,6 +87,7 @@ class User(Object):
self.is_mutual_contact = is_mutual_contact self.is_mutual_contact = is_mutual_contact
self.is_deleted = is_deleted self.is_deleted = is_deleted
self.is_bot = is_bot self.is_bot = is_bot
self.status = status
self.first_name = first_name self.first_name = first_name
self.last_name = last_name self.last_name = last_name
self.username = username self.username = username

View File

@ -0,0 +1,84 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2018 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 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