Merge develop -> asyncio

This commit is contained in:
Dan 2019-08-03 10:48:35 +02:00
commit 7badcd38b4
78 changed files with 245 additions and 283 deletions

4
.gitignore vendored
View File

@ -11,6 +11,9 @@ pyrogram/api/all.py
# PyCharm stuff
.idea/
# VS Code
.vscode/
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
@ -78,6 +81,7 @@ instance/
# Sphinx documentation
docs/_build/
docs/source/_build
# PyBuilder
target/

View File

@ -478,7 +478,6 @@ def start():
f.write("\n 0xbc799737: \"pyrogram.api.core.BoolFalse\",")
f.write("\n 0x997275b5: \"pyrogram.api.core.BoolTrue\",")
f.write("\n 0x56730bcc: \"pyrogram.api.core.Null\",")
f.write("\n 0x1cb5c415: \"pyrogram.api.core.Vector\",")
f.write("\n 0x73f1f8dc: \"pyrogram.api.core.MsgContainer\",")
f.write("\n 0xae500895: \"pyrogram.api.core.FutureSalts\",")

View File

@ -192,7 +192,6 @@ dismissed and their IP addresses are now kept as aliases to the nearest one.
Thanks to `@FrayxRulez <https://t.me/tgbetachat/104921>`_ for telling about alias DCs.
I want to migrate my account from DCX to DCY.
---------------------------------------------
@ -246,9 +245,13 @@ The error in question is ``[400 PEER_ID_INVALID]``, and could mean several thing
- The chat id you tried to use is simply wrong, double check it.
- The chat id refers to a group or channel you are not a member of.
- The chat id refers to a user you have't seen yet (from contacts, groups in common, forwarded messages or private
chats).
- The chat id argument you passed is in form of a string; you have to convert it into an integer with ``int(chat_id)``.
- The chat id refers to a user your current session haven't met yet.
About the last point: in order for you to meet a user and thus communicate with them, you should ask yourself how to
contact people using official apps. The answer is the same for Pyrogram too and involves normal usages such as searching
for usernames, meet them in a common group, have their phone contacts saved, getting a message mentioning them (either a
forward or a mention in the message text).
UnicodeEncodeError: '<encoding>' codec can't encode …
-----------------------------------------------------
@ -258,6 +261,14 @@ shows up when you try to print something and has very little to do with Pyrogram
your own terminal. To fix it, either find a way to change the encoding settings of your terminal to UTF-8 or switch to a
better terminal altogether.
Uploading with URLs gives error WEBPAGE_CURL_FAILED
---------------------------------------------------
When uploading media files using an URL, the server automatically tries to download the media and uploads it to the
Telegram cloud. This error usually happens in case the provided URL is not publicly accessible by Telegram itself or the
media exceeds 20 MB in size. In such cases, your only option is to download the media yourself and upload from your
local machine.
My verification code expires immediately!
-----------------------------------------

View File

@ -45,8 +45,9 @@ Welcome to Pyrogram
topics/tgcrypto
topics/storage-engines
topics/text-formatting
topics/serialize
topics/serializing
topics/proxy
topics/scheduling
topics/bots-interaction
topics/mtproto-vs-botapi
topics/debugging

View File

@ -0,0 +1,87 @@
Scheduling Tasks
================
Scheduling tasks means executing one or more functions periodically at pre-defined intervals or after a delay. This is
useful, for example, to send recurring messages to specific chats or users.
Since there's no built-in task scheduler in Pyrogram, this page will only show examples on how to integrate Pyrogram
with the main Python schedule libraries such as ``schedule`` and ``apscheduler``. For more detailed information, you can
visit and learn from each library documentation.
Using ``schedule``
------------------
- Install with ``pip3 install schedule``
- Documentation: https://schedule.readthedocs.io
.. code-block:: python
import time
import schedule
from pyrogram import Client
app = Client("my_account")
def job():
app.send_message("me", "Hi!")
schedule.every(3).seconds.do(job)
with app:
while True:
schedule.run_pending()
time.sleep(1)
Using ``apscheduler``
---------------------
- Install with ``pip3 install apscheduler``
- Documentation: https://apscheduler.readthedocs.io
.. code-block:: python
from apscheduler.schedulers.background import BackgroundScheduler
from pyrogram import Client
app = Client("my_account")
def job():
app.send_message("me", "Hi!")
scheduler = BackgroundScheduler()
scheduler.add_job(job, "interval", seconds=3)
scheduler.start()
app.run()
``apscheduler`` does also support async code, here's an example with
`Pyrogram Asyncio <https://docs.pyrogram.org/intro/install.html#asynchronous>`_:
.. code-block:: python
from apscheduler.schedulers.asyncio import AsyncIOScheduler
from pyrogram import Client
app = Client("my_account")
async def job():
await app.send_message("me", "Hi!")
scheduler = AsyncIOScheduler()
scheduler.add_job(job, "interval", seconds=3)
scheduler.start()
app.run()

View File

@ -19,8 +19,7 @@
from importlib import import_module
from .all import objects
from .core.tl_object import TLObject
for k, v in objects.items():
path, name = v.rsplit(".", 1)
TLObject.all[k] = getattr(import_module(path), name)
objects[k] = getattr(import_module(path), name)

View File

@ -22,8 +22,5 @@ from .gzip_packed import GzipPacked
from .list import List
from .message import Message
from .msg_container import MsgContainer
from .primitives import (
Bool, BoolTrue, BoolFalse, Bytes, Double,
Int, Long, Int128, Int256, Null, String, Vector
)
from .primitives import *
from .tl_object import TLObject

View File

@ -16,10 +16,11 @@
# 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 .bool import Bool, BoolTrue, BoolFalse
from .bool import Bool, BoolFalse, BoolTrue
from .bytes import Bytes
from .double import Double
from .int import Int, Long, Int128, Int256
from .null import Null
from .string import String
from .vector import Vector
__all__ = ["Bool", "BoolFalse", "BoolTrue", "Bytes", "Double", "Int", "Long", "Int128", "Int256", "String", "Vector"]

View File

@ -1,32 +0,0 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2019 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 io import BytesIO
from ..tl_object import TLObject
class Null(TLObject):
ID = 0x56730bcc
@staticmethod
def read(b: BytesIO, *args) -> None:
return None
def __new__(cls) -> bytes:
return cls.ID.to_bytes(4, "little")

View File

@ -20,17 +20,17 @@ from collections import OrderedDict
from io import BytesIO
from json import dumps
from ..all import objects
class TLObject:
all = {}
__slots__ = []
QUALNAME = "Base"
@staticmethod
def read(b: BytesIO, *args): # TODO: Rename b -> data
return TLObject.all[int.from_bytes(b.read(4), "little")].read(b, *args)
return objects[int.from_bytes(b.read(4), "little")].read(b, *args)
def write(self, *args) -> bytes:
pass

View File

@ -1031,7 +1031,7 @@ class Client(Methods, BaseClient):
access_hash = 0
peer_type = "group"
elif isinstance(peer, (types.Channel, types.ChannelForbidden)):
peer_id = int("-100" + str(peer.id))
peer_id = utils.get_channel_id(peer.id)
access_hash = peer.access_hash
username = getattr(peer, "username", None)
@ -1139,7 +1139,7 @@ class Client(Methods, BaseClient):
try:
diff = await self.send(
functions.updates.GetChannelDifference(
channel=await self.resolve_peer(int("-100" + str(channel_id))),
channel=await self.resolve_peer(utils.get_channel_id(channel_id)),
filter=types.ChannelMessagesFilter(
ranges=[types.MessageRange(
min_id=update.message.id,
@ -1527,33 +1527,38 @@ class Client(Methods, BaseClient):
except KeyError:
raise PeerIdInvalid
if peer_id > 0:
peer_type = utils.get_type(peer_id)
if peer_type == "user":
self.fetch_peers(
await self.send(
functions.users.GetUsers(
id=[types.InputUser(
user_id=peer_id,
access_hash=0
)]
id=[
types.InputUser(
user_id=peer_id,
access_hash=0
)
]
)
)
)
elif peer_type == "chat":
await self.send(
functions.messages.GetChats(
id=[-peer_id]
)
)
else:
if str(peer_id).startswith("-100"):
await self.send(
functions.channels.GetChannels(
id=[types.InputChannel(
channel_id=int(str(peer_id)[4:]),
await self.send(
functions.channels.GetChannels(
id=[
types.InputChannel(
channel_id=utils.get_channel_id(peer_id),
access_hash=0
)]
)
)
else:
await self.send(
functions.messages.GetChats(
id=[-peer_id]
)
)
]
)
)
try:
return self.storage.get_peer_by_id(peer_id)

View File

@ -171,8 +171,13 @@ class Dispatcher:
args = None
if isinstance(handler, handler_type):
if handler.check(parsed_update):
args = (parsed_update,)
try:
if handler.check(parsed_update):
args = (parsed_update,)
except Exception as e:
log.error(e, exc_info=True)
continue
elif isinstance(handler, RawUpdateHandler):
args = (update, users, chats)

View File

@ -21,10 +21,11 @@ import base64
import struct
import sys
from concurrent.futures.thread import ThreadPoolExecutor
from typing import Union, List
from typing import List
from typing import Union
import pyrogram
from pyrogram.api.types import PeerUser, PeerChat, PeerChannel
from . import BaseClient
from ...api import types
@ -33,10 +34,17 @@ def decode(s: str) -> bytes:
s = base64.urlsafe_b64decode(s + "=" * (-len(s) % 4))
r = b""
assert s[-1] == 2
try:
assert s[-1] == 2
skip = 1
except AssertionError:
assert s[-2] == 22
assert s[-1] == 4
skip = 2
i = 0
while i < len(s) - 1:
while i < len(s) - skip:
if s[i] != 0:
r += bytes([s[i]])
else:
@ -52,7 +60,7 @@ def encode(s: bytes) -> str:
r = b""
n = 0
for i in s + bytes([2]):
for i in s + bytes([22]) + bytes([4]):
if i == 0:
n += 1
else:
@ -74,23 +82,6 @@ async def ainput(prompt: str = ""):
)).rstrip()
def get_peer_id(input_peer) -> int:
return (
input_peer.user_id if isinstance(input_peer, types.InputPeerUser)
else -input_peer.chat_id if isinstance(input_peer, types.InputPeerChat)
else int("-100" + str(input_peer.channel_id))
)
def get_input_peer(peer_id: int, access_hash: int):
return (
types.InputPeerUser(user_id=peer_id, access_hash=access_hash) if peer_id > 0
else types.InputPeerChannel(channel_id=int(str(peer_id)[4:]), access_hash=access_hash)
if (str(peer_id).startswith("-100") and access_hash)
else types.InputPeerChat(chat_id=-peer_id)
)
def get_offset_date(dialogs):
for m in reversed(dialogs.messages):
if isinstance(m, types.MessageEmpty):
@ -195,7 +186,7 @@ def parse_deleted_messages(client, update) -> List["pyrogram.Message"]:
pyrogram.Message(
message_id=message,
chat=pyrogram.Chat(
id=int("-100" + str(channel_id)),
id=get_channel_id(channel_id),
type="channel",
client=client
) if channel_id is not None else None,
@ -215,3 +206,39 @@ def unpack_inline_message_id(inline_message_id: str) -> types.InputBotInlineMess
id=r[1],
access_hash=r[2]
)
MIN_CHANNEL_ID = -1002147483647
MAX_CHANNEL_ID = -1000000000000
MIN_CHAT_ID = -2147483647
MAX_USER_ID = 2147483647
def get_peer_id(peer: Union[PeerUser, PeerChat, PeerChannel]) -> int:
if isinstance(peer, PeerUser):
return peer.user_id
if isinstance(peer, PeerChat):
return -peer.chat_id
if isinstance(peer, PeerChannel):
return MAX_CHANNEL_ID - peer.channel_id
raise ValueError("Peer type invalid: {}".format(peer))
def get_type(peer_id: int) -> str:
if peer_id < 0:
if MIN_CHAT_ID <= peer_id:
return "chat"
if MIN_CHANNEL_ID <= peer_id < MAX_CHANNEL_ID:
return "channel"
elif 0 < peer_id <= MAX_USER_ID:
return "user"
raise ValueError("Peer id invalid: {}".format(peer_id))
def get_channel_id(peer_id: int) -> int:
return MAX_CHANNEL_ID - peer_id

View File

@ -20,7 +20,7 @@ from typing import Union
import pyrogram
from pyrogram.api import functions, types
from ...ext import BaseClient
from ...ext import BaseClient, utils
class GetChat(BaseClient):
@ -70,7 +70,7 @@ class GetChat(BaseClient):
chat_id = -r.chat.id
if isinstance(r.chat, types.Channel):
chat_id = int("-100" + str(r.chat.id))
chat_id = utils.get_channel_id(r.chat.id)
peer = await self.resolve_peer(chat_id)

View File

@ -24,7 +24,7 @@ import pyrogram
from pyrogram.api import functions, types
from pyrogram.errors import FloodWait
from ...ext import BaseClient
from ...ext import BaseClient, utils
log = logging.getLogger(__name__)
@ -101,10 +101,8 @@ class GetDialogs(BaseClient):
chat_id = to_id.user_id
else:
chat_id = message.from_id
elif isinstance(to_id, types.PeerChat):
chat_id = -to_id.chat_id
else:
chat_id = int("-100" + str(to_id.channel_id))
chat_id = utils.get_peer_id(to_id)
messages[chat_id] = await pyrogram.Message._parse(self, message, users, chats)

View File

@ -19,7 +19,6 @@
from typing import Union
from pyrogram.api import functions
from ...ext import BaseClient

View File

@ -21,7 +21,6 @@ from typing import List, Union
from pyrogram.api import functions, types
from pyrogram.client.ext import utils
from ...ext import BaseClient

View File

@ -21,7 +21,6 @@ from typing import Union, List
import pyrogram
from pyrogram.api import functions, types
from pyrogram.client.ext import utils
from ...ext import BaseClient

View File

@ -19,7 +19,6 @@
from typing import Union
from pyrogram.api import functions, types
from ...ext import BaseClient

View File

@ -19,7 +19,6 @@
from typing import Union
from pyrogram.api import functions
from ...ext import BaseClient

View File

@ -16,4 +16,4 @@
# 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 .parser import Parser
from .parser import Parser

View File

@ -16,6 +16,6 @@
# 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 .memory_storage import MemoryStorage
from .file_storage import FileStorage
from .memory_storage import MemoryStorage
from .storage import Storage

View File

@ -25,7 +25,5 @@ class CallbackGame(Object):
Use BotFather to set up your game.
"""
__slots__ = []
def __init__(self):
super().__init__()

View File

@ -25,6 +25,7 @@ from pyrogram.api import types
from ..object import Object
from ..update import Update
from ..user_and_chats import User
from ...ext import utils
class CallbackQuery(Object, Update):
@ -60,8 +61,6 @@ class CallbackQuery(Object, Update):
"""
__slots__ = ["id", "from_user", "chat_instance", "message", "inline_message_id", "data", "game_short_name"]
def __init__(
self,
*,
@ -90,16 +89,7 @@ class CallbackQuery(Object, Update):
inline_message_id = None
if isinstance(callback_query, types.UpdateBotCallbackQuery):
peer = callback_query.peer
if isinstance(peer, types.PeerUser):
peer_id = peer.user_id
elif isinstance(peer, types.PeerChat):
peer_id = -peer.chat_id
else:
peer_id = int("-100" + str(peer.channel_id))
message = await client.get_messages(peer_id, callback_query.msg_id)
message = await client.get_messages(utils.get_peer_id(callback_query.peer), callback_query.msg_id)
elif isinstance(callback_query, types.UpdateInlineBotCallbackQuery):
inline_message_id = b64encode(
pack(

View File

@ -37,8 +37,6 @@ class ForceReply(Object):
2) if the bot's message is a reply (has reply_to_message_id), sender of the original message.
"""
__slots__ = ["selective"]
def __init__(
self,
selective: bool = None

View File

@ -37,8 +37,6 @@ class GameHighScore(Object):
Position in high score table for the game.
"""
__slots__ = ["user", "score", "position"]
def __init__(
self,
*,

View File

@ -23,6 +23,7 @@ from pyrogram.api.types import (
KeyboardButtonUrl, KeyboardButtonCallback,
KeyboardButtonSwitchInline, KeyboardButtonGame
)
from .callback_game import CallbackGame
from ..object import Object
@ -59,10 +60,6 @@ class InlineKeyboardButton(Object):
# TODO: Add callback_game and pay fields
__slots__ = [
"text", "url", "callback_data", "switch_inline_query", "switch_inline_query_current_chat", "callback_game"
]
def __init__(
self,
text: str,

View File

@ -19,6 +19,7 @@
from typing import List
from pyrogram.api.types import ReplyInlineMarkup, KeyboardButtonRow
from . import InlineKeyboardButton
from ..object import Object
@ -31,8 +32,6 @@ class InlineKeyboardMarkup(Object):
List of button rows, each represented by a List of InlineKeyboardButton objects.
"""
__slots__ = ["inline_keyboard"]
def __init__(
self,
inline_keyboard: List[List[InlineKeyboardButton]]

View File

@ -41,8 +41,6 @@ class KeyboardButton(Object):
Available in private chats only.
"""
__slots__ = ["text", "request_contact", "request_location"]
def __init__(
self,
text: str,

View File

@ -20,6 +20,7 @@ from typing import List, Union
from pyrogram.api.types import KeyboardButtonRow
from pyrogram.api.types import ReplyKeyboardMarkup as RawReplyKeyboardMarkup
from . import KeyboardButton
from ..object import Object
@ -49,8 +50,6 @@ class ReplyKeyboardMarkup(Object):
select the new language. Other users in the group don't see the keyboard.
"""
__slots__ = ["keyboard", "resize_keyboard", "one_time_keyboard", "selective"]
def __init__(
self,
keyboard: List[List[Union[KeyboardButton, str]]],

View File

@ -38,8 +38,6 @@ class ReplyKeyboardRemove(Object):
keyboard for that user, while still showing the keyboard with poll options to users who haven't voted yet.
"""
__slots__ = ["selective"]
def __init__(
self,
selective: bool = None

View File

@ -48,7 +48,6 @@ class InlineQuery(Object, Update):
location (:obj:`Location`. *optional*):
Sender location, only for bots that request user location.
"""
__slots__ = ["id", "from_user", "query", "offset", "location"]
def __init__(
self,

View File

@ -53,8 +53,6 @@ class InlineQueryResult(Object):
- :obj:`InlineQueryResultAnimation`
"""
__slots__ = ["type", "id", "input_message_content", "reply_markup"]
def __init__(
self,
type: str,

View File

@ -68,11 +68,6 @@ class InlineQueryResultAnimation(InlineQueryResult):
Content of the message to be sent instead of the photo.
"""
__slots__ = [
"animation_url", "thumb_url", "title", "description", "caption", "parse_mode", "reply_markup",
"input_message_content"
]
def __init__(
self,
animation_url: str,

View File

@ -49,8 +49,6 @@ class InlineQueryResultArticle(InlineQueryResult):
Inline keyboard attached to the message.
"""
__slots__ = ["title", "url", "description", "thumb_url"]
def __init__(
self,
title: str,

View File

@ -68,11 +68,6 @@ class InlineQueryResultPhoto(InlineQueryResult):
Content of the message to be sent instead of the photo.
"""
__slots__ = [
"photo_url", "thumb_url", "title", "description", "caption", "parse_mode", "reply_markup",
"input_message_content"
]
def __init__(
self,
photo_url: str,

View File

@ -30,7 +30,6 @@ class InputMedia(Object):
- :obj:`InputMediaPhoto`
- :obj:`InputMediaVideo`
"""
__slots__ = ["media", "caption", "parse_mode"]
def __init__(self, media: str, caption: str, parse_mode: str):
super().__init__()

View File

@ -56,8 +56,6 @@ class InputMediaAnimation(InputMedia):
Animation duration.
"""
__slots__ = ["thumb", "width", "height", "duration"]
def __init__(
self,
media: str,

View File

@ -58,8 +58,6 @@ class InputMediaAudio(InputMedia):
Title of the audio
"""
__slots__ = ["thumb", "duration", "performer", "title"]
def __init__(
self,
media: str,

View File

@ -47,8 +47,6 @@ class InputMediaDocument(InputMedia):
Pass None to completely disable style parsing.
"""
__slots__ = ["thumb"]
def __init__(
self,
media: str,

View File

@ -43,8 +43,6 @@ class InputMediaPhoto(InputMedia):
Pass None to completely disable style parsing.
"""
__slots__ = []
def __init__(
self,
media: str,

View File

@ -61,8 +61,6 @@ class InputMediaVideo(InputMedia):
Pass True, if the uploaded video is suitable for streaming.
"""
__slots__ = ["thumb", "width", "height", "duration", "supports_streaming"]
def __init__(
self,
media: str,

View File

@ -37,8 +37,6 @@ class InputPhoneContact(Object):
Contact's last name
"""
__slots__ = []
def __init__(self, phone: str, first_name: str, last_name: str = ""):
super().__init__(None)

View File

@ -31,8 +31,6 @@ class InputMessageContent(Object):
- :obj:`InputTextMessageContent`
"""
__slots__ = []
def __init__(self):
super().__init__()

View File

@ -41,8 +41,6 @@ class InputTextMessageContent(InputMessageContent):
Disables link previews for links in this message.
"""
__slots__ = ["message_text", "parse_mode", "disable_web_page_preview"]
def __init__(self, message_text: str, parse_mode: Union[str, None] = object, disable_web_page_preview: bool = None):
super().__init__()

View File

@ -58,8 +58,6 @@ class Animation(Object):
Animation thumbnails.
"""
__slots__ = ["file_id", "file_name", "mime_type", "file_size", "date", "width", "height", "duration", "thumbs"]
def __init__(
self,
*,

View File

@ -58,10 +58,6 @@ class Audio(Object):
Thumbnails of the music file album cover.
"""
__slots__ = [
"file_id", "file_name", "mime_type", "file_size", "date", "duration", "performer", "title", "thumbs"
]
def __init__(
self,
*,

View File

@ -42,8 +42,6 @@ class Contact(Object):
Additional data about the contact in the form of a vCard.
"""
__slots__ = ["phone_number", "first_name", "last_name", "user_id", "vcard"]
def __init__(
self,
*,

View File

@ -49,8 +49,6 @@ class Document(Object):
Document thumbnails as defined by sender.
"""
__slots__ = ["file_id", "file_name", "mime_type", "file_size", "date", "thumbs"]
def __init__(
self,
*,

View File

@ -48,8 +48,6 @@ class Game(Object):
Upload via BotFather.
"""
__slots__ = ["id", "title", "short_name", "description", "photo", "animation"]
def __init__(
self,
*,

View File

@ -33,8 +33,6 @@ class Location(Object):
Latitude as defined by sender.
"""
__slots__ = ["longitude", "latitude"]
def __init__(
self,
*,

View File

@ -31,7 +31,8 @@ from ..object import Object
from ..update import Update
from ..user_and_chats.chat import Chat
from ..user_and_chats.user import User
from ...parser import utils, Parser
from ...ext import utils
from ...parser import utils as parser_utils, Parser
class Str(str):
@ -54,7 +55,7 @@ class Str(str):
return Parser.unparse(self, self.entities, True)
def __getitem__(self, item):
return utils.remove_surrogates(utils.add_surrogates(self)[item])
return parser_utils.remove_surrogates(parser_utils.add_surrogates(self)[item])
class Message(Object, Update):
@ -263,17 +264,6 @@ class Message(Object, Update):
# TODO: Add game missing field. Also invoice, successful_payment, connected_website
__slots__ = [
"message_id", "date", "chat", "from_user", "forward_from", "forward_sender_name", "forward_from_chat",
"forward_from_message_id", "forward_signature", "forward_date", "reply_to_message", "mentioned", "empty",
"service", "media", "edit_date", "media_group_id", "author_signature", "text", "entities", "caption_entities",
"audio", "document", "photo", "sticker", "animation", "game", "video", "voice", "video_note", "caption",
"contact", "location", "venue", "web_page", "poll", "new_chat_members", "left_chat_member", "new_chat_title",
"new_chat_photo", "delete_chat_photo", "group_chat_created", "supergroup_chat_created", "channel_chat_created",
"migrate_to_chat_id", "migrate_from_chat_id", "pinned_message", "game_high_score", "views", "via_bot",
"outgoing", "matches", "command", "reply_markup"
]
def __init__(
self,
*,
@ -446,7 +436,7 @@ class Message(Object, Update):
new_chat_title=new_chat_title,
new_chat_photo=new_chat_photo,
delete_chat_photo=delete_chat_photo,
migrate_to_chat_id=int("-100" + str(migrate_to_chat_id)) if migrate_to_chat_id else None,
migrate_to_chat_id=utils.get_channel_id(migrate_to_chat_id) if migrate_to_chat_id else None,
migrate_from_chat_id=-migrate_from_chat_id if migrate_from_chat_id else None,
group_chat_created=group_chat_created,
channel_chat_created=channel_chat_created,
@ -602,10 +592,26 @@ class Message(Object, Update):
date=message.date,
chat=Chat._parse(client, message, users, chats),
from_user=User._parse(client, users.get(message.from_id, None)),
text=Str(message.message).init(entities) or None if media is None else None,
caption=Str(message.message).init(entities) or None if media is not None else None,
entities=entities or None if media is None else None,
caption_entities=entities or None if media is not None else None,
text=(
Str(message.message).init(entities) or None
if media is None or web_page is not None
else None
),
caption=(
Str(message.message).init(entities) or None
if media is not None and web_page is None
else None
),
entities=(
entities or None
if media is None or web_page is not None
else None
),
caption_entities=(
entities or None
if media is not None and web_page is None
else None
),
author_signature=message.post_author,
forward_from=forward_from,
forward_sender_name=forward_sender_name,

View File

@ -47,8 +47,6 @@ class MessageEntity(Object):
For "text_mention" only, the mentioned user.
"""
__slots__ = ["type", "offset", "length", "url", "user"]
ENTITIES = {
types.MessageEntityMention.ID: "mention",
types.MessageEntityHashtag.ID: "hashtag",

View File

@ -49,8 +49,6 @@ class Photo(Object):
Available thumbnails of this photo.
"""
__slots__ = ["file_id", "width", "height", "file_size", "date", "thumbs"]
def __init__(
self,
*,

View File

@ -48,8 +48,6 @@ class Poll(Object, Update):
Index of your chosen option (0-9), None in case you haven't voted yet.
"""
__slots__ = ["id", "question", "options", "is_closed", "total_voters", "chosen_option"]
def __init__(
self,
*,

View File

@ -35,8 +35,6 @@ class PollOption(Object):
The data this poll option is holding.
"""
__slots__ = ["text", "voter_count", "data"]
def __init__(
self,
*,

View File

@ -66,10 +66,6 @@ class Sticker(Object):
# TODO: Add mask position
__slots__ = [
"file_id", "file_name", "mime_type", "file_size", "date", "width", "height", "emoji", "set_name", "thumbs"
]
def __init__(
self,
*,

View File

@ -29,8 +29,6 @@ class StrippedThumbnail(Object):
Thumbnail data
"""
__slots__ = ["data"]
def __init__(
self,
*,

View File

@ -43,8 +43,6 @@ class Thumbnail(Object):
File size.
"""
__slots__ = ["file_id", "width", "height", "file_size"]
def __init__(
self,
*,

View File

@ -44,8 +44,6 @@ class Venue(Object):
"""
__slots__ = ["location", "title", "address", "foursquare_id", "foursquare_type"]
def __init__(
self,
*,

View File

@ -61,11 +61,6 @@ class Video(Object):
Video thumbnails.
"""
__slots__ = [
"file_id", "width", "height", "duration", "file_name", "mime_type", "supports_streaming", "file_size", "date",
"thumbs"
]
def __init__(
self,
*,

View File

@ -52,8 +52,6 @@ class VideoNote(Object):
Video thumbnails.
"""
__slots__ = ["file_id", "mime_type", "file_size", "date", "length", "duration", "thumbs"]
def __init__(
self,
*,

View File

@ -47,8 +47,6 @@ class Voice(Object):
Date the voice was sent in Unix time.
"""
__slots__ = ["file_id", "duration", "waveform", "mime_type", "file_size", "date"]
def __init__(
self,
*,

View File

@ -83,12 +83,6 @@ class WebPage(Object):
Author of the webpage, eg the Twitter user for a tweet, or the author in an article.
"""
__slots__ = [
"id", "url", "display_url", "type", "site_name", "title", "description",
"audio", "document", "photo", "animation", "video",
"embed_url", "embed_type", "embed_width", "embed_height", "duration", "author"
]
def __init__(
self,
*,

View File

@ -29,8 +29,6 @@ class Meta(type, metaclass=type("", (type,), {"__str__": lambda _: "~hi"})):
class Object(metaclass=Meta):
__slots__ = ["_client"]
def __init__(self, client: "pyrogram.BaseClient" = None):
self._client = client
@ -50,7 +48,7 @@ class Object(metaclass=Meta):
else (attr, str(datetime.fromtimestamp(getattr(obj, attr))))
if attr.endswith("date")
else (attr, getattr(obj, attr))
for attr in getattr(obj, "__slots__", [])
for attr in filter(lambda x: not x.startswith("_"), obj.__dict__)
if getattr(obj, attr) is not None
]
)
@ -63,13 +61,13 @@ class Object(metaclass=Meta):
self.__class__.__name__,
", ".join(
"{}={}".format(attr, repr(getattr(self, attr)))
for attr in self.__slots__
for attr in filter(lambda x: not x.startswith("_"), self.__dict__)
if getattr(self, attr) is not None
)
)
def __eq__(self, other: "Object") -> bool:
for attr in self.__slots__:
for attr in self.__dict__:
try:
if getattr(self, attr) != getattr(other, attr):
return False

View File

@ -26,8 +26,6 @@ class ContinuePropagation(StopAsyncIteration):
class Update:
__slots__ = []
def stop_propagation(self):
raise StopPropagation

View File

@ -23,6 +23,7 @@ from pyrogram.api import types
from .chat_permissions import ChatPermissions
from .chat_photo import ChatPhoto
from ..object import Object
from ...ext import utils
class Chat(Object):
@ -94,12 +95,6 @@ class Chat(Object):
Information about the chat default permissions, for groups and supergroups.
"""
__slots__ = [
"id", "type", "is_verified", "is_restricted", "is_scam", "is_support", "title", "username", "first_name",
"last_name", "photo", "description", "invite_link", "pinned_message", "sticker_set_name", "can_set_sticker_set",
"members_count", "restriction_reason", "permissions"
]
def __init__(
self,
*,
@ -180,7 +175,7 @@ class Chat(Object):
@staticmethod
def _parse_channel_chat(client, channel: types.Channel) -> "Chat":
peer_id = int("-100" + str(channel.id))
peer_id = utils.get_channel_id(channel.id)
return Chat(
id=peer_id,

View File

@ -54,8 +54,6 @@ class ChatMember(Object):
Information about the member permissions.
"""
__slots__ = ["user", "status", "date", "is_member", "invited_by", "promoted_by", "restricted_by", "permissions"]
def __init__(
self,
*,

View File

@ -94,13 +94,6 @@ class ChatPermissions(Object):
True, if polls can be sent, implies can_send_media_messages.
"""
__slots__ = [
"until_date", "can_be_edited", "can_change_info", "can_post_messages", "can_edit_messages",
"can_delete_messages", "can_restrict_members", "can_invite_users", "can_pin_messages", "can_promote_members",
"can_send_messages", "can_send_media_messages", "can_send_other_messages", "can_add_web_page_previews",
"can_send_polls"
]
def __init__(
self,
*,

View File

@ -35,8 +35,6 @@ class ChatPhoto(Object):
Unique file identifier of big (640x640) chat photo. This file_id can be used only for photo download.
"""
__slots__ = ["small_file_id", "big_file_id"]
def __init__(
self,
*,

View File

@ -45,8 +45,6 @@ class ChatPreview(Object):
Preview of some of the chat members.
"""
__slots__ = ["title", "type", "members_count", "photo", "members"]
def __init__(
self,
*,

View File

@ -21,6 +21,7 @@ import pyrogram
from pyrogram.api import types
from ..object import Object
from ..user_and_chats import Chat
from ...ext import utils
class Dialog(Object):
@ -46,8 +47,6 @@ class Dialog(Object):
True, if the dialog is pinned.
"""
__slots__ = ["chat", "top_message", "unread_messages_count", "unread_mentions_count", "unread_mark", "is_pinned"]
def __init__(
self,
*,
@ -70,18 +69,9 @@ class Dialog(Object):
@staticmethod
def _parse(client, dialog: types.Dialog, messages, users, chats) -> "Dialog":
chat_id = dialog.peer
if isinstance(chat_id, types.PeerUser):
chat_id = chat_id.user_id
elif isinstance(chat_id, types.PeerChat):
chat_id = -chat_id.chat_id
else:
chat_id = int("-100" + str(chat_id.channel_id))
return Dialog(
chat=Chat._parse_dialog(client, dialog.peer, users, chats),
top_message=messages.get(chat_id),
top_message=messages.get(utils.get_peer_id(dialog.peer)),
unread_messages_count=dialog.unread_count,
unread_mentions_count=dialog.unread_mentions_count,
unread_mark=dialog.unread_mark,

View File

@ -106,12 +106,6 @@ class User(Object, Update):
This field is available only in case *is_restricted* is True.
"""
__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", "last_online_date", "next_offline_date",
"username", "language_code", "dc_id", "phone_number", "photo", "restriction_reason"
]
def __init__(
self,
*,

View File

@ -21,8 +21,9 @@ from datetime import datetime
from importlib import import_module
from typing import Type
from pyrogram.api.core import TLObject
from pyrogram.api.types import RpcError as RawRPCError
from pyrogram.api.core import TLObject
from .exceptions.all import exceptions

View File

@ -28,7 +28,6 @@ from pyrogram.api import functions, types
from pyrogram.api.core import TLObject, Long, Int
from pyrogram.connection import Connection
from pyrogram.crypto import AES, RSA, Prime
from .internals import MsgId
log = logging.getLogger(__name__)

View File

@ -16,9 +16,10 @@
# 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 Message, MsgContainer, TLObject
from pyrogram.api.functions import Ping
from pyrogram.api.types import MsgsAck, HttpWait
from pyrogram.api.core import Message, MsgContainer, TLObject
from .msg_id import MsgId
from .seq_no import SeqNo

View File

@ -22,15 +22,15 @@ from datetime import datetime, timedelta
from hashlib import sha1
from io import BytesIO
from pyrogram.api.all import layer
import pyrogram
from pyrogram import __copyright__, __license__, __version__
from pyrogram.api import functions, types
from pyrogram.api.all import layer
from pyrogram.api.core import TLObject, MsgContainer, Int, Long, FutureSalt, FutureSalts
from pyrogram.connection import Connection
from pyrogram.crypto import MTProto
from pyrogram.errors import RPCError, InternalServerError, AuthKeyDuplicated
from .internals import MsgId, MsgFactory
log = logging.getLogger(__name__)