Merge branch 'user-status' into develop
This commit is contained in:
commit
8a81bd1221
@ -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")
|
||||||
|
|
||||||
|
@ -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:
|
||||||
|
|
||||||
|
@ -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
|
||||||
)
|
)
|
||||||
|
@ -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
|
||||||
)
|
)
|
||||||
|
@ -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:
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
54
pyrogram/client/handlers/user_status_handler.py
Normal file
54
pyrogram/client/handlers/user_status_handler.py
Normal 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
|
||||||
|
)
|
@ -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
|
||||||
|
41
pyrogram/client/methods/decorators/on_user_status.py
Normal file
41
pyrogram/client/methods/decorators/on_user_status.py
Normal 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
|
@ -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
|
||||||
)
|
)
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
84
pyrogram/client/types/user_and_chats/user_status.py
Normal file
84
pyrogram/client/types/user_and_chats/user_status.py
Normal 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
|
Loading…
Reference in New Issue
Block a user