2
0
mirror of https://github.com/pyrogram/pyrogram synced 2025-09-01 14:55:12 +00:00

Merge branch 'develop' into session_storage

# Conflicts:
#	pyrogram/client/client.py
#	pyrogram/client/ext/base_client.py
#	pyrogram/client/ext/syncer.py
#	pyrogram/client/style/html.py
#	pyrogram/client/style/markdown.py
This commit is contained in:
bakatrouble
2019-04-14 21:49:45 +03:00
231 changed files with 7200 additions and 1929 deletions

View File

@@ -24,25 +24,13 @@ if sys.version_info[:3] in [(3, 5, 0), (3, 5, 1), (3, 5, 2)]:
# Monkey patch the standard "typing" module because Python versions from 3.5.0 to 3.5.2 have a broken one.
sys.modules["typing"] = typing
__copyright__ = "Copyright (C) 2017-2019 Dan Tès <https://github.com/delivrance>".replace(
"\xe8",
"e" if sys.getfilesystemencoding() != "utf-8" else "\xe8"
)
__version__ = "0.12.0"
__license__ = "GNU Lesser General Public License v3 or later (LGPLv3+)"
__version__ = "0.11.1.develop"
__copyright__ = "Copyright (C) 2017-2019 Dan Tès <https://github.com/delivrance>".replace(
"\xe8", "e" if sys.getfilesystemencoding() != "utf-8" else "\xe8"
)
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, User, UserStatus,
UserProfilePhotos, Venue, Animation, Video, VideoNote, Voice, CallbackQuery, Messages, ForceReply,
InlineKeyboardButton, InlineKeyboardMarkup, KeyboardButton, ReplyKeyboardMarkup, ReplyKeyboardRemove,
Poll, PollOption, ChatPreview, StopPropagation, ContinuePropagation, Game, CallbackGame, GameHighScore,
GameHighScores
)
from .client import (
Client, ChatAction, ParseMode, Emoji,
MessageHandler, DeletedMessagesHandler, CallbackQueryHandler,
RawUpdateHandler, DisconnectHandler, UserStatusHandler, Filters
)
from .errors import RPCError
from .client import *
from .client.handlers import *
from .client.types import *

View File

@@ -26,6 +26,10 @@ from .primitives import Int, Long
class FutureSalt(Object):
ID = 0x0949d9dc
__slots__ = ["valid_since", "valid_until", "salt"]
QUALNAME = "FutureSalt"
def __init__(self, valid_since: int or datetime, valid_until: int or datetime, salt: int):
self.valid_since = valid_since
self.valid_until = valid_until

View File

@@ -27,6 +27,10 @@ from .primitives import Int, Long
class FutureSalts(Object):
ID = 0xae500895
__slots__ = ["req_msg_id", "now", "salts"]
QUALNAME = "FutureSalts"
def __init__(self, req_msg_id: int, now: int or datetime, salts: list):
self.req_msg_id = req_msg_id
self.now = now

View File

@@ -26,6 +26,10 @@ from .primitives import Int, Bytes
class GzipPacked(Object):
ID = 0x3072cfa1
__slots__ = ["packed_data"]
QUALNAME = "GzipPacked"
def __init__(self, packed_data: Object):
self.packed_data = packed_data

View File

@@ -25,6 +25,10 @@ from .primitives import Int, Long
class Message(Object):
ID = 0x5bb8e511 # hex(crc32(b"message msg_id:long seqno:int bytes:int body:Object = Message"))
__slots__ = ["msg_id", "seq_no", "length", "body"]
QUALNAME = "Message"
def __init__(self, body: Object, msg_id: int, seq_no: int, length: int):
self.msg_id = msg_id
self.seq_no = seq_no

View File

@@ -26,6 +26,10 @@ from .primitives import Int
class MsgContainer(Object):
ID = 0x73f1f8dc
__slots__ = ["messages"]
QUALNAME = "MsgContainer"
def __init__(self, messages: list):
self.messages = messages

View File

@@ -19,14 +19,16 @@
from collections import OrderedDict
from datetime import datetime
from io import BytesIO
from json import JSONEncoder, dumps
from ..all import objects
from json import dumps
class Object:
all = {}
__slots__ = []
QUALNAME = "Base"
@staticmethod
def read(b: BytesIO, *args):
return Object.all[int.from_bytes(b.read(4), "little")].read(b, *args)
@@ -35,20 +37,11 @@ class Object:
pass
def __str__(self) -> str:
return dumps(self, cls=Encoder, indent=4)
def __bool__(self) -> bool:
return True
def __eq__(self, other) -> bool:
return self.__dict__ == other.__dict__
return dumps(self, indent=4, default=default, ensure_ascii=False)
def __len__(self) -> int:
return len(self.write())
def __call__(self):
pass
def __getitem__(self, item):
return getattr(self, item)
@@ -62,29 +55,18 @@ def remove_none(obj):
return obj
class Encoder(JSONEncoder):
def default(self, o: Object):
try:
content = o.__dict__
except AttributeError:
if isinstance(o, datetime):
return o.strftime("%d-%b-%Y %H:%M:%S")
else:
return repr(o)
def default(o: "Object"):
try:
content = {i: getattr(o, i) for i in o.__slots__}
name = o.__class__.__name__
o = objects.get(getattr(o, "ID", None), None)
if o is not None:
if o.startswith("pyrogram.client"):
r = remove_none(OrderedDict([("_", "pyrogram:" + name)] + [i for i in content.items()]))
r.pop("_client", None)
return r
else:
return OrderedDict(
[("_", o.replace("pyrogram.api.types.", "telegram:"))]
+ [i for i in content.items()]
)
return remove_none(
OrderedDict(
[("_", o.QUALNAME)]
+ [i for i in content.items()]
)
)
except AttributeError:
if isinstance(o, datetime):
return o.strftime("%d-%b-%Y %H:%M:%S")
else:
return None
return repr(o)

View File

@@ -19,8 +19,7 @@
from .client import Client
from .ext import BaseClient, ChatAction, Emoji, ParseMode
from .filters import Filters
from .handlers import (
MessageHandler, DeletedMessagesHandler,
CallbackQueryHandler, RawUpdateHandler,
DisconnectHandler, UserStatusHandler
)
__all__ = [
"Client", "BaseClient", "ChatAction", "Emoji", "ParseMode", "Filters",
]

View File

@@ -39,7 +39,11 @@ from typing import Union, List, Type
from pyrogram.api import functions, types
from pyrogram.api.core import Object
from pyrogram.api.errors import (
from pyrogram.client.handlers import DisconnectHandler
from pyrogram.client.handlers.handler import Handler
from pyrogram.client.methods.password.utils import compute_check
from pyrogram.crypto import AES
from pyrogram.errors import (
PhoneMigrate, NetworkMigrate, PhoneNumberInvalid,
PhoneNumberUnoccupied, PhoneCodeInvalid, PhoneCodeHashEmpty,
PhoneCodeExpired, PhoneCodeEmpty, SessionPasswordNeeded,
@@ -47,13 +51,8 @@ from pyrogram.api.errors import (
VolumeLocNotFound, UserMigrate, FileIdInvalid, ChannelPrivate, PhoneNumberOccupied,
PasswordRecoveryNa, PasswordEmpty
)
from pyrogram.client.handlers import DisconnectHandler
from pyrogram.client.handlers.handler import Handler
from pyrogram.client.methods.password.utils import compute_check
from pyrogram.crypto import AES
from pyrogram.session import Auth, Session
from .dispatcher import Dispatcher
from .ext import utils, Syncer, BaseClient
from .ext import utils, Syncer, BaseClient, Dispatcher
from .methods import Methods
from .session_storage import (
SessionDoesNotExist, SessionStorage, MemorySessionStorage, JsonSessionStorage,
@@ -70,9 +69,10 @@ class Client(Methods, BaseClient):
Args:
session_name (``str``):
Name to uniquely identify a session of either a User or a Bot, e.g.: "my_main_account".
You still can use bot token here, but it will be deprecated in next release.
Note: as long as a valid User session file exists, Pyrogram won't ask you again to input your phone number.
Name to uniquely identify a session of either a User or a Bot, e.g.: "my_account". This name will be used
to save a file to disk that stores details needed for reconnecting without asking again for credentials.
Note for bots: You can pass a bot token here, but this usage will be deprecated in next releases.
Use *bot_token* instead.
api_id (``int``, *optional*):
The *api_id* part of your Telegram API Key, as integer. E.g.: 12345
@@ -182,31 +182,35 @@ class Client(Methods, BaseClient):
Defaults to False (normal session).
"""
def __init__(self,
session_name: Union[str, SessionStorage],
api_id: Union[int, str] = None,
api_hash: str = None,
app_version: str = None,
device_model: str = None,
system_version: str = None,
lang_code: str = None,
ipv6: bool = False,
proxy: dict = None,
test_mode: bool = False,
phone_number: str = None,
phone_code: Union[str, callable] = None,
password: str = None,
recovery_code: callable = None,
force_sms: bool = False,
bot_token: str = None,
first_name: str = None,
last_name: str = None,
workers: int = BaseClient.WORKERS,
workdir: str = BaseClient.WORKDIR,
config_file: str = BaseClient.CONFIG_FILE,
plugins: dict = None,
no_updates: bool = None,
takeout: bool = None):
terms_of_service_displayed = False
def __init__(
self,
session_name: str,
api_id: Union[int, str] = None,
api_hash: str = None,
app_version: str = None,
device_model: str = None,
system_version: str = None,
lang_code: str = None,
ipv6: bool = False,
proxy: dict = None,
test_mode: bool = False,
phone_number: str = None,
phone_code: Union[str, callable] = None,
password: str = None,
recovery_code: callable = None,
force_sms: bool = False,
bot_token: str = None,
first_name: str = None,
last_name: str = None,
workers: int = BaseClient.WORKERS,
workdir: str = BaseClient.WORKDIR,
config_file: str = BaseClient.CONFIG_FILE,
plugins: dict = None,
no_updates: bool = None,
takeout: bool = None
):
if isinstance(session_name, str):
if session_name == ':memory:':
@@ -222,6 +226,8 @@ class Client(Methods, BaseClient):
super().__init__(session_storage)
super().__init__(session_storage)
self.session_name = str(session_name) # TODO: build correct session name
self.api_id = int(api_id) if api_id else None
self.api_hash = api_hash
@@ -277,7 +283,7 @@ class Client(Methods, BaseClient):
Requires no parameters.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
``ConnectionError`` in case you try to start an already started Client.
"""
if self.is_started:
@@ -288,10 +294,10 @@ class Client(Methods, BaseClient):
self.session_storage.is_bot = True
self.bot_token = self.session_storage._session_name
self.session_storage._session_name = self.session_storage._session_name.split(":")[0]
warnings.warn('\nYou are using a bot token as session name.\n'
'It will be deprecated in next update, please use session file name to load '
'existing sessions and bot_token argument to create new sessions.',
DeprecationWarning, stacklevel=2)
warnings.warn('\nWARNING: You are using a bot token as session name!\n'
'This usage will be deprecated soon. Please use a session file name to load '
'an existing session and the bot_token argument to create new sessions.\n'
'More info: https://docs.pyrogram.ml/start/Setup#bot-authorization\n')
self.load_config()
self.load_session()
@@ -309,6 +315,7 @@ class Client(Methods, BaseClient):
try:
if self.session_storage.user_id is None:
if self.bot_token is None:
self.is_bot = False
self.authorize_user()
else:
self.session_storage.is_bot = True
@@ -446,7 +453,7 @@ class Client(Methods, BaseClient):
Requires no parameters.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
"""
self.start()
self.idle()
@@ -558,9 +565,10 @@ class Client(Methods, BaseClient):
try:
r = self.send(
functions.auth.SendCode(
self.phone_number,
self.api_id,
self.api_hash
phone_number=self.phone_number,
api_id=self.api_id,
api_hash=self.api_hash,
settings=types.CodeSettings()
)
)
except (PhoneMigrate, NetworkMigrate) as e:
@@ -604,8 +612,9 @@ class Client(Methods, BaseClient):
phone_code_hash = r.phone_code_hash
terms_of_service = r.terms_of_service
if terms_of_service:
if terms_of_service and not Client.terms_of_service_displayed:
print("\n" + terms_of_service.text + "\n")
Client.terms_of_service_displayed = True
if self.force_sms:
self.send(
@@ -640,9 +649,9 @@ class Client(Methods, BaseClient):
try:
r = self.send(
functions.auth.SignIn(
self.phone_number,
phone_code_hash,
self.phone_code
phone_number=self.phone_number,
phone_code_hash=phone_code_hash,
phone_code=self.phone_code
)
)
except PhoneNumberUnoccupied:
@@ -653,11 +662,11 @@ class Client(Methods, BaseClient):
try:
r = self.send(
functions.auth.SignUp(
self.phone_number,
phone_code_hash,
self.phone_code,
self.first_name,
self.last_name
phone_number=self.phone_number,
phone_code_hash=phone_code_hash,
phone_code=self.phone_code,
first_name=self.first_name,
last_name=self.last_name
)
)
except PhoneNumberOccupied:
@@ -751,7 +760,11 @@ class Client(Methods, BaseClient):
break
if terms_of_service:
assert self.send(functions.help.AcceptTermsOfService(terms_of_service.id))
assert self.send(
functions.help.AcceptTermsOfService(
id=terms_of_service.id
)
)
self.password = None
self.session_storage.user_id = r.user.id
@@ -992,16 +1005,16 @@ class Client(Methods, BaseClient):
Timeout in seconds.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
"""
if not self.is_started:
raise ConnectionError("Client has not been started")
if self.no_updates:
data = functions.InvokeWithoutUpdates(data)
data = functions.InvokeWithoutUpdates(query=data)
if self.takeout_id:
data = functions.InvokeWithTakeout(self.takeout_id, data)
data = functions.InvokeWithTakeout(takeout_id=self.takeout_id, query=data)
r = self.session.send(data, retries, timeout)
@@ -1118,7 +1131,7 @@ class Client(Methods, BaseClient):
try:
module = import_module(module_path)
except ModuleNotFoundError:
except ImportError:
log.warning('[LOAD] Ignoring non-existent module "{}"'.format(module_path))
continue
@@ -1154,7 +1167,7 @@ class Client(Methods, BaseClient):
try:
module = import_module(module_path)
except ModuleNotFoundError:
except ImportError:
log.warning('[UNLOAD] Ignoring non-existent module "{}"'.format(module_path))
continue
@@ -1241,7 +1254,7 @@ class Client(Methods, BaseClient):
On success, the resolved peer id is returned in form of an InputPeer object.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
``KeyError`` in case the peer doesn't exist in the internal database.
"""
try:
@@ -1276,7 +1289,7 @@ class Client(Methods, BaseClient):
self.fetch_peers(
self.send(
functions.users.GetUsers(
id=[types.InputUser(peer_id, 0)]
id=[types.InputUser(user_id=peer_id, access_hash=0)]
)
)
)
@@ -1284,7 +1297,7 @@ class Client(Methods, BaseClient):
if str(peer_id).startswith("-100"):
self.send(
functions.channels.GetChannels(
id=[types.InputChannel(int(str(peer_id)[4:]), 0)]
id=[types.InputChannel(channel_id=int(str(peer_id)[4:]), access_hash=0)]
)
)
else:
@@ -1348,7 +1361,7 @@ class Client(Methods, BaseClient):
On success, the uploaded file is returned in form of an InputFile object.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
"""
part_size = 512 * 1024
file_size = os.path.getsize(path)
@@ -1591,8 +1604,8 @@ class Client(Methods, BaseClient):
hashes = session.send(
functions.upload.GetCdnFileHashes(
r.file_token,
offset
file_token=r.file_token,
offset=offset
)
)

View File

@@ -18,6 +18,7 @@
from .base_client import BaseClient
from .chat_action import ChatAction
from .dispatcher import Dispatcher
from .emoji import Emoji
from .parse_mode import ParseMode
from .syncer import Syncer

View File

@@ -74,8 +74,8 @@ class BaseClient:
self.rnd_id = MsgId
self.channels_pts = {}
self.markdown = Markdown(self.session_storage)
self.html = HTML(self.session_storage)
self.markdown = Markdown(self.session_storage, self)
self.html = HTML(self.session_storage, self)
self.session = None
self.media_sessions = {}
@@ -122,3 +122,6 @@ class BaseClient:
def get_chat_members_count(self, *args, **kwargs):
pass
def answer_inline_query(self, *args, **kwargs):
pass

View File

@@ -24,7 +24,10 @@ from threading import Thread
import pyrogram
from pyrogram.api import types
from ..handlers import CallbackQueryHandler, MessageHandler, RawUpdateHandler, UserStatusHandler, DeletedMessagesHandler
from ..handlers import (
CallbackQueryHandler, MessageHandler, DeletedMessagesHandler,
UserStatusHandler, RawUpdateHandler, InlineQueryHandler
)
log = logging.getLogger(__name__)
@@ -73,7 +76,10 @@ class Dispatcher:
(types.UpdateUserStatus,):
lambda upd, usr, cht: (
pyrogram.UserStatus._parse(self.client, upd.status, upd.user_id), UserStatusHandler
)
),
(types.UpdateBotInlineQuery,):
lambda upd, usr, cht: (pyrogram.InlineQuery._parse(self.client, upd, usr), InlineQueryHandler)
}
self.update_parsers = {key: value for key_tuple, value in self.update_parsers.items() for key in key_tuple}

View File

@@ -67,10 +67,10 @@ def get_peer_id(input_peer) -> int:
def get_input_peer(peer_id: int, access_hash: int):
return (
types.InputPeerUser(peer_id, access_hash) if peer_id > 0
else types.InputPeerChannel(int(str(peer_id)[4:]), access_hash)
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(-peer_id)
else types.InputPeerChat(chat_id=-peer_id)
)

View File

@@ -115,7 +115,7 @@ class Filters:
voice = create("Voice", lambda _, m: bool(m.voice))
"""Filter messages that contain :obj:`Voice <pyrogram.Voice>` note objects."""
video_note = create("Voice", lambda _, m: bool(m.video_note))
video_note = create("VideoNote", lambda _, m: bool(m.video_note))
"""Filter messages that contain :obj:`VideoNote <pyrogram.VideoNote>` objects."""
contact = create("Contact", lambda _, m: bool(m.contact))
@@ -222,14 +222,16 @@ class Filters:
- poll"""
@staticmethod
def command(command: str or list,
prefix: str or list = "/",
separator: str = " ",
case_sensitive: bool = False):
def command(
commands: str or list,
prefix: str or list = "/",
separator: str = " ",
case_sensitive: bool = False
):
"""Filter commands, i.e.: text messages starting with "/" or any other custom prefix.
Args:
command (``str`` | ``list``):
commands (``str`` | ``list``):
The command or list of commands as string the filter should look for.
Examples: "start", ["start", "help", "settings"]. When a message text containing
a command arrives, the command itself and its arguments will be stored in the *command*
@@ -249,31 +251,25 @@ class Filters:
Examples: when True, command="Start" would trigger /Start but not /start.
"""
def f(_, m):
if m.text:
for i in _.p:
if m.text.startswith(i):
t = m.text.split(_.s)
c, a = t[0][len(i):], t[1:]
c = c if _.cs else c.lower()
m.command = ([c] + a) if c in _.c else None
def func(flt, message):
text = message.text or message.caption
if text:
for p in flt.p:
if text.startswith(p):
s = text.split(flt.s)
c, a = s[0][len(p):], s[1:]
c = c if flt.cs else c.lower()
message.command = ([c] + a) if c in flt.c else None
break
return bool(m.command)
return bool(message.command)
return create(
"Command",
f,
c={command if case_sensitive
else command.lower()}
if not isinstance(command, list)
else {c if case_sensitive
else c.lower()
for c in command},
p=set(prefix) if prefix else {""},
s=separator,
cs=case_sensitive
)
commands = commands if type(commands) is list else [commands]
commands = {c if case_sensitive else c.lower() for c in commands}
prefixes = set(prefix) if prefix else {""}
return create("Command", func=func, c=commands, p=prefixes, s=separator, cs=case_sensitive)
@staticmethod
def regex(pattern, flags: int = 0):
@@ -311,21 +307,20 @@ class Filters:
def __init__(self, users: int or str or list = None):
users = [] if users is None else users if type(users) is list else [users]
super().__init__(
{"me" if i in ["me", "self"] else i.lower().strip("@") if type(i) is str else i for i in users}
if type(users) is list else
{"me" if users in ["me", "self"] else users.lower().strip("@") if type(users) is str else users}
"me" if u in ["me", "self"]
else u.lower().strip("@") if type(u) is str
else u for u in users
)
def __call__(self, message):
return bool(
message.from_user
and (message.from_user.id in self
or (message.from_user.username
and message.from_user.username.lower() in self)
or ("me" in self
and message.from_user.is_self))
)
return (message.from_user
and (message.from_user.id in self
or (message.from_user.username
and message.from_user.username.lower() in self)
or ("me" in self
and message.from_user.is_self)))
# noinspection PyPep8Naming
class chat(Filter, set):
@@ -343,21 +338,21 @@ class Filters:
def __init__(self, chats: int or str or list = None):
chats = [] if chats is None else chats if type(chats) is list else [chats]
super().__init__(
{"me" if i in ["me", "self"] else i.lower().strip("@") if type(i) is str else i for i in chats}
if type(chats) is list else
{"me" if chats in ["me", "self"] else chats.lower().strip("@") if type(chats) is str else chats}
"me" if c in ["me", "self"]
else c.lower().strip("@") if type(c) is str
else c for c in chats
)
def __call__(self, message):
return bool(
message.chat
and (message.chat.id in self
or (message.chat.username
and message.chat.username.lower() in self)
or ("me" in self and message.from_user
and message.from_user.is_self
and not message.outgoing))
)
return (message.chat
and (message.chat.id in self
or (message.chat.username
and message.chat.username.lower() in self)
or ("me" in self
and message.from_user
and message.from_user.is_self
and not message.outgoing)))
dan = create("Dan", lambda _, m: bool(m.from_user and m.from_user.id == 23122162))

View File

@@ -19,6 +19,12 @@
from .callback_query_handler import CallbackQueryHandler
from .deleted_messages_handler import DeletedMessagesHandler
from .disconnect_handler import DisconnectHandler
from .inline_query_handler import InlineQueryHandler
from .message_handler import MessageHandler
from .raw_update_handler import RawUpdateHandler
from .user_status_handler import UserStatusHandler
__all__ = [
"MessageHandler", "DeletedMessagesHandler", "CallbackQueryHandler", "RawUpdateHandler", "DisconnectHandler",
"UserStatusHandler", "InlineQueryHandler"
]

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 InlineQueryHandler(Handler):
"""The InlineQuery handler class. Used to handle inline queries.
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_inline_query() <pyrogram.Client.on_inline_query>` decorator.
Args:
callback (``callable``):
Pass a function that will be called when a new InlineQuery arrives. It takes *(client, inline_query)*
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 inline queries 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 inline query handler.
inline_query (:obj:`InlineQuery <pyrogram.InlineQuery>`):
The received inline query.
"""
def __init__(self, callback: callable, filters=None):
super().__init__(callback, filters)
def check(self, callback_query):
return (
self.filters(callback_query)
if callable(self.filters)
else True
)

View File

@@ -17,6 +17,7 @@
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
from .answer_callback_query import AnswerCallbackQuery
from .answer_inline_query import AnswerInlineQuery
from .get_game_high_scores import GetGameHighScores
from .get_inline_bot_results import GetInlineBotResults
from .request_callback_answer import RequestCallbackAnswer
@@ -27,6 +28,7 @@ from .set_game_score import SetGameScore
class Bots(
AnswerCallbackQuery,
AnswerInlineQuery,
GetInlineBotResults,
RequestCallbackAnswer,
SendInlineBotResult,

View File

@@ -21,12 +21,14 @@ from pyrogram.client.ext import BaseClient
class AnswerCallbackQuery(BaseClient):
def answer_callback_query(self,
callback_query_id: str,
text: str = None,
show_alert: bool = None,
url: str = None,
cache_time: int = 0):
def answer_callback_query(
self,
callback_query_id: str,
text: str = None,
show_alert: bool = None,
url: str = None,
cache_time: int = 0
):
"""Use this method to send answers to callback queries sent from inline keyboards.
The answer will be displayed to the user as a notification at the top of the chat screen or as an alert.
@@ -55,7 +57,7 @@ class AnswerCallbackQuery(BaseClient):
True, on success.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
"""
return self.send(
functions.messages.SetBotCallbackAnswer(

View File

@@ -0,0 +1,91 @@
# 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 typing import List
from pyrogram.api import functions, types
from pyrogram.client.ext import BaseClient
from ...types.inline_mode import InlineQueryResult
class AnswerInlineQuery(BaseClient):
def answer_inline_query(
self,
inline_query_id: str,
results: List[InlineQueryResult],
cache_time: int = 300,
is_personal: bool = None,
next_offset: str = "",
switch_pm_text: str = "",
switch_pm_parameter: str = ""
):
"""Use this method to send answers to an inline query.
No more than 50 results per query are allowed.
Args:
inline_query_id (``str``):
Unique identifier for the answered query.
results (List of :obj:`InlineQueryResult <pyrogram.InlineQueryResult>`):
A list of results for the inline query.
cache_time (``int``, *optional*):
The maximum amount of time in seconds that the result of the inline query may be cached on the server.
Defaults to 300.
is_personal (``bool``, *optional*):
Pass True, if results may be cached on the server side only for the user that sent the query.
By default, results may be returned to any user who sends the same query.
next_offset (``str``, *optional*):
Pass the offset that a client should send in the next query with the same text to receive more results.
Pass an empty string if there are no more results or if you dont support pagination.
Offset length cant exceed 64 bytes.
switch_pm_text (``str``, *optional*):
If passed, clients will display a button with specified text that switches the user to a private chat
with the bot and sends the bot a start message with the parameter switch_pm_parameter
switch_pm_parameter (``str``, *optional*):
`Deep-linking <https://core.telegram.org/bots#deep-linking>`_ parameter for the /start message sent to
the bot when user presses the switch button. 1-64 characters, only A-Z, a-z, 0-9, _ and - are allowed.
Example: An inline bot that sends YouTube videos can ask the user to connect the bot to their YouTube
account to adapt search results accordingly. To do this, it displays a "Connect your YouTube account"
button above the results, or even before showing any. The user presses the button, switches to a private
chat with the bot and, in doing so, passes a start parameter that instructs the bot to return an oauth
link. Once done, the bot can offer a switch_inline button so that the user can easily return to the chat
where they wanted to use the bot's inline capabilities.
Returns:
On success, True is returned.
"""
return self.send(
functions.messages.SetInlineBotResults(
query_id=int(inline_query_id),
results=[r.write() for r in results],
cache_time=cache_time,
gallery=None,
private=is_personal or None,
next_offset=next_offset or None,
switch_pm=types.InlineBotSwitchPM(
text=switch_pm_text,
start_param=switch_pm_parameter
) if switch_pm_text else None
)
)

View File

@@ -24,10 +24,12 @@ from pyrogram.client.ext import BaseClient
class GetGameHighScores(BaseClient):
def get_game_high_scores(self,
user_id: Union[int, str],
chat_id: Union[int, str],
message_id: int = None):
def get_game_high_scores(
self,
user_id: Union[int, str],
chat_id: Union[int, str],
message_id: int = None
):
"""Use this method to get data for high score tables.
Args:
@@ -50,7 +52,7 @@ class GetGameHighScores(BaseClient):
On success, a :obj:`GameHighScores <pyrogram.GameHighScores>` object is returned.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
"""
# TODO: inline_message_id

View File

@@ -19,17 +19,19 @@
from typing import Union
from pyrogram.api import functions, types
from pyrogram.api.errors import UnknownError
from pyrogram.errors import UnknownError
from pyrogram.client.ext import BaseClient
class GetInlineBotResults(BaseClient):
def get_inline_bot_results(self,
bot: Union[int, str],
query: str,
offset: str = "",
latitude: float = None,
longitude: float = None):
def get_inline_bot_results(
self,
bot: Union[int, str],
query: str,
offset: str = "",
latitude: float = None,
longitude: float = None
):
"""Use this method to get bot results via inline queries.
You can then send a result using :obj:`send_inline_bot_result <pyrogram.Client.send_inline_bot_result>`
@@ -56,7 +58,7 @@ class GetInlineBotResults(BaseClient):
On Success, :obj:`BotResults <pyrogram.api.types.messages.BotResults>` is returned.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
``TimeoutError`` if the bot fails to answer within 10 seconds
"""
# TODO: Don't return the raw type

View File

@@ -23,12 +23,14 @@ from pyrogram.client.ext import BaseClient
class RequestCallbackAnswer(BaseClient):
def request_callback_answer(self,
chat_id: Union[int, str],
message_id: int,
callback_data: bytes):
"""Use this method to request a callback answer from bots. This is the equivalent of clicking an
inline button containing callback data.
def request_callback_answer(
self,
chat_id: Union[int, str],
message_id: int,
callback_data: bytes
):
"""Use this method to request a callback answer from bots.
This is the equivalent of clicking an inline button containing callback data.
Args:
chat_id (``int`` | ``str``):
@@ -47,7 +49,7 @@ class RequestCallbackAnswer(BaseClient):
or as an alert.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
``TimeoutError`` if the bot fails to answer within 10 seconds.
"""
return self.send(

View File

@@ -24,15 +24,19 @@ from pyrogram.client.ext import BaseClient
class SendGame(BaseClient):
def send_game(self,
chat_id: Union[int, str],
game_short_name: str,
disable_notification: bool = None,
reply_to_message_id: int = None,
reply_markup: Union["pyrogram.InlineKeyboardMarkup",
"pyrogram.ReplyKeyboardMarkup",
"pyrogram.ReplyKeyboardRemove",
"pyrogram.ForceReply"] = None) -> "pyrogram.Message":
def send_game(
self,
chat_id: Union[int, str],
game_short_name: str,
disable_notification: bool = None,
reply_to_message_id: int = None,
reply_markup: Union[
"pyrogram.InlineKeyboardMarkup",
"pyrogram.ReplyKeyboardMarkup",
"pyrogram.ReplyKeyboardRemove",
"pyrogram.ForceReply"
] = None
) -> "pyrogram.Message":
"""Use this method to send a game.
Args:
@@ -59,7 +63,7 @@ class SendGame(BaseClient):
On success, the sent :obj:`Message` is returned.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
"""
r = self.send(
functions.messages.SendMedia(

View File

@@ -23,13 +23,15 @@ from pyrogram.client.ext import BaseClient
class SendInlineBotResult(BaseClient):
def send_inline_bot_result(self,
chat_id: Union[int, str],
query_id: int,
result_id: str,
disable_notification: bool = None,
reply_to_message_id: int = None,
hide_via: bool = None):
def send_inline_bot_result(
self,
chat_id: Union[int, str],
query_id: int,
result_id: str,
disable_notification: bool = None,
reply_to_message_id: int = None,
hide_via: bool = None
):
"""Use this method to send an inline bot result.
Bot results can be retrieved using :obj:`get_inline_bot_results <pyrogram.Client.get_inline_bot_results>`
@@ -59,7 +61,7 @@ class SendInlineBotResult(BaseClient):
On success, the sent Message is returned.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
"""
return self.send(
functions.messages.SendInlineBotResult(

View File

@@ -24,13 +24,15 @@ from pyrogram.client.ext import BaseClient
class SetGameScore(BaseClient):
def set_game_score(self,
user_id: Union[int, str],
score: int,
force: bool = None,
disable_edit_message: bool = None,
chat_id: Union[int, str] = None,
message_id: int = None):
def set_game_score(
self,
user_id: Union[int, str],
score: int,
force: bool = None,
disable_edit_message: bool = None,
chat_id: Union[int, str] = None,
message_id: int = None
):
# inline_message_id: str = None): TODO Add inline_message_id
"""Use this method to set the score of the specified user in a game.
@@ -65,7 +67,7 @@ class SetGameScore(BaseClient):
otherwise returns True.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
:class:`BotScoreNotModified` if the new score is not greater than the user's current score in the chat and force is False.
"""
r = self.send(

View File

@@ -31,12 +31,14 @@ from .kick_chat_member import KickChatMember
from .leave_chat import LeaveChat
from .pin_chat_message import PinChatMessage
from .promote_chat_member import PromoteChatMember
from .restrict_chat import RestrictChat
from .restrict_chat_member import RestrictChatMember
from .set_chat_description import SetChatDescription
from .set_chat_photo import SetChatPhoto
from .set_chat_title import SetChatTitle
from .unban_chat_member import UnbanChatMember
from .unpin_chat_message import UnpinChatMessage
from .update_chat_username import UpdateChatUsername
class Chats(
@@ -60,6 +62,8 @@ class Chats(
GetChatMembersCount,
GetChatPreview,
IterDialogs,
IterChatMembers
IterChatMembers,
UpdateChatUsername,
RestrictChat
):
pass

View File

@@ -23,8 +23,10 @@ from ...ext import BaseClient
class DeleteChatPhoto(BaseClient):
def delete_chat_photo(self,
chat_id: Union[int, str]) -> bool:
def delete_chat_photo(
self,
chat_id: Union[int, str]
) -> bool:
"""Use this method to delete a chat photo.
Photos can't be changed for private chats.
You must be an administrator in the chat for this to work and must have the appropriate admin rights.
@@ -41,7 +43,7 @@ class DeleteChatPhoto(BaseClient):
True on success.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
``ValueError`` if a chat_id belongs to user.
"""
peer = self.resolve_peer(chat_id)

View File

@@ -23,8 +23,10 @@ from ...ext import BaseClient
class ExportChatInviteLink(BaseClient):
def export_chat_invite_link(self,
chat_id: Union[int, str]) -> str:
def export_chat_invite_link(
self,
chat_id: Union[int, str]
) -> str:
"""Use this method to generate a new invite link for a chat; any previously generated link is revoked.
You must be an administrator in the chat for this to work and have the appropriate admin rights.
@@ -38,14 +40,14 @@ class ExportChatInviteLink(BaseClient):
On success, the exported invite link as string is returned.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
"""
peer = self.resolve_peer(chat_id)
if isinstance(peer, types.InputPeerChat):
return self.send(
functions.messages.ExportChatInvite(
chat_id=peer.chat_id
peer=peer.chat_id
)
).link
elif isinstance(peer, types.InputPeerChannel):

View File

@@ -24,10 +24,13 @@ from ...ext import BaseClient
class GetChat(BaseClient):
def get_chat(self,
chat_id: Union[int, str]) -> "pyrogram.Chat":
"""Use this method to get up to date information about the chat (current name of the user for
one-on-one conversations, current username of a user, group or channel, etc.)
def get_chat(
self,
chat_id: Union[int, str]
) -> "pyrogram.Chat":
"""Use this method to get up to date information about the chat.
Information include current name of the user for one-on-one conversations, current username of a user, group or
channel, etc.
Args:
chat_id (``int`` | ``str``):
@@ -39,7 +42,7 @@ class GetChat(BaseClient):
On success, a :obj:`Chat <pyrogram.Chat>` object is returned.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
``ValueError`` in case the chat invite link refers to a chat you haven't joined yet.
"""
match = self.INVITE_LINK_RE.match(str(chat_id))
@@ -67,10 +70,10 @@ class GetChat(BaseClient):
peer = self.resolve_peer(chat_id)
if isinstance(peer, types.InputPeerChannel):
r = self.send(functions.channels.GetFullChannel(peer))
r = self.send(functions.channels.GetFullChannel(channel=peer))
elif isinstance(peer, (types.InputPeerUser, types.InputPeerSelf)):
r = self.send(functions.users.GetFullUser(peer))
r = self.send(functions.users.GetFullUser(id=peer))
else:
r = self.send(functions.messages.GetFullChat(peer.chat_id))
r = self.send(functions.messages.GetFullChat(chat_id=peer.chat_id))
return pyrogram.Chat._parse_full(self, r)

View File

@@ -19,14 +19,17 @@
from typing import Union
import pyrogram
from pyrogram.api import functions, types, errors
from pyrogram.api import functions, types
from pyrogram.errors import UserNotParticipant
from ...ext import BaseClient
class GetChatMember(BaseClient):
def get_chat_member(self,
chat_id: Union[int, str],
user_id: Union[int, str]) -> "pyrogram.ChatMember":
def get_chat_member(
self,
chat_id: Union[int, str],
user_id: Union[int, str]
) -> "pyrogram.ChatMember":
"""Use this method to get information about one member of a chat.
Args:
@@ -42,7 +45,7 @@ class GetChatMember(BaseClient):
On success, a :obj:`ChatMember <pyrogram.ChatMember>` object is returned.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
"""
chat_id = self.resolve_peer(chat_id)
user_id = self.resolve_peer(user_id)
@@ -55,10 +58,10 @@ class GetChatMember(BaseClient):
)
for member in pyrogram.ChatMembers._parse(self, full_chat).chat_members:
if member.user.id == user_id.user_id:
if member.user.is_self:
return member
else:
raise errors.UserNotParticipant
raise UserNotParticipant
elif isinstance(chat_id, types.InputPeerChannel):
r = self.send(
functions.channels.GetParticipant(

View File

@@ -16,12 +16,17 @@
# 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 logging
import time
from typing import Union
import pyrogram
from pyrogram.api import functions, types
from pyrogram.errors import FloodWait
from ...ext import BaseClient
log = logging.getLogger(__name__)
class Filters:
ALL = "all"
@@ -33,12 +38,14 @@ class Filters:
class GetChatMembers(BaseClient):
def get_chat_members(self,
chat_id: Union[int, str],
offset: int = 0,
limit: int = 200,
query: str = "",
filter: str = Filters.ALL) -> "pyrogram.ChatMembers":
def get_chat_members(
self,
chat_id: Union[int, str],
offset: int = 0,
limit: int = 200,
query: str = "",
filter: str = Filters.ALL
) -> "pyrogram.ChatMembers":
"""Use this method to get a chunk of the members list of a chat.
You can get up to 200 chat members at once.
@@ -82,7 +89,7 @@ class GetChatMembers(BaseClient):
On success, a :obj:`ChatMembers` object is returned.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
``ValueError`` if you used an invalid filter or a chat_id that belongs to a user.
"""
peer = self.resolve_peer(chat_id)
@@ -92,7 +99,7 @@ class GetChatMembers(BaseClient):
self,
self.send(
functions.messages.GetFullChat(
peer.chat_id
chat_id=peer.chat_id
)
)
)
@@ -114,17 +121,22 @@ class GetChatMembers(BaseClient):
else:
raise ValueError("Invalid filter \"{}\"".format(filter))
return pyrogram.ChatMembers._parse(
self,
self.send(
functions.channels.GetParticipants(
channel=peer,
filter=filter,
offset=offset,
limit=limit,
hash=0
while True:
try:
return pyrogram.ChatMembers._parse(
self,
self.send(
functions.channels.GetParticipants(
channel=peer,
filter=filter,
offset=offset,
limit=limit,
hash=0
)
)
)
)
)
except FloodWait as e:
log.warning("Sleeping for {}s".format(e.x))
time.sleep(e.x)
else:
raise ValueError("The chat_id \"{}\" belongs to a user".format(chat_id))

View File

@@ -23,8 +23,10 @@ from ...ext import BaseClient
class GetChatMembersCount(BaseClient):
def get_chat_members_count(self,
chat_id: Union[int, str]) -> int:
def get_chat_members_count(
self,
chat_id: Union[int, str]
) -> int:
"""Use this method to get the number of members in a chat.
Args:
@@ -35,7 +37,7 @@ class GetChatMembersCount(BaseClient):
On success, an integer is returned.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
``ValueError`` if a chat_id belongs to user.
"""
peer = self.resolve_peer(chat_id)

View File

@@ -22,8 +22,10 @@ from ...ext import BaseClient
class GetChatPreview(BaseClient):
def get_chat_preview(self,
invite_link: str):
def get_chat_preview(
self,
invite_link: str
):
"""Use this method to get the preview of a chat using the invite link.
This method only returns a chat preview, if you want to join a chat use :meth:`join_chat`
@@ -36,7 +38,7 @@ class GetChatPreview(BaseClient):
Either :obj:`Chat` or :obj:`ChatPreview`, depending on whether you already joined the chat or not.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
``ValueError`` in case of an invalid invite_link.
"""
match = self.INVITE_LINK_RE.match(invite_link)

View File

@@ -21,18 +21,20 @@ import time
import pyrogram
from pyrogram.api import functions, types
from pyrogram.api.errors import FloodWait
from pyrogram.errors import FloodWait
from ...ext import BaseClient
log = logging.getLogger(__name__)
class GetDialogs(BaseClient):
def get_dialogs(self,
offset_date: int = 0,
limit: int = 100,
pinned_only: bool = False) -> "pyrogram.Dialogs":
"""Use this method to get a chunk of the user's dialogs
def get_dialogs(
self,
offset_date: int = 0,
limit: int = 100,
pinned_only: bool = False
) -> "pyrogram.Dialogs":
"""Use this method to get a chunk of the user's dialogs.
You can get up to 100 dialogs at once.
For a more convenient way of getting a user's dialogs see :meth:`iter_dialogs`.
@@ -54,7 +56,7 @@ class GetDialogs(BaseClient):
On success, a :obj:`Dialogs` object is returned.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
"""
while True:

View File

@@ -20,6 +20,7 @@ from string import ascii_lowercase
from typing import Union, Generator
import pyrogram
from pyrogram.api import types
from ...ext import BaseClient
@@ -37,11 +38,13 @@ QUERYABLE_FILTERS = (Filters.ALL, Filters.KICKED, Filters.RESTRICTED)
class IterChatMembers(BaseClient):
def iter_chat_members(self,
chat_id: Union[int, str],
limit: int = 0,
query: str = "",
filter: str = Filters.ALL) -> Generator["pyrogram.ChatMember", None, None]:
def iter_chat_members(
self,
chat_id: Union[int, str],
limit: int = 0,
query: str = "",
filter: str = Filters.ALL
) -> Generator["pyrogram.ChatMember", None, None]:
"""Use this method to iterate through the members of a chat sequentially.
This convenience method does the same as repeatedly calling :meth:`get_chat_members` in a loop, thus saving you
@@ -75,13 +78,14 @@ class IterChatMembers(BaseClient):
A generator yielding :obj:`ChatMember <pyrogram.ChatMember>` objects.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
"""
current = 0
yielded = set()
queries = [query] if query else QUERIES
total = limit or (1 << 31) - 1
limit = min(200, total)
resolved_chat_id = self.resolve_peer(chat_id)
filter = (
Filters.RECENT
@@ -107,6 +111,9 @@ class IterChatMembers(BaseClient):
if not chat_members:
break
if isinstance(resolved_chat_id, types.InputPeerChat):
total = len(chat_members)
offset += len(chat_members)
for chat_member in chat_members:

View File

@@ -23,9 +23,11 @@ from ...ext import BaseClient
class IterDialogs(BaseClient):
def iter_dialogs(self,
offset_date: int = 0,
limit: int = 0) -> Generator["pyrogram.Dialog", None, None]:
def iter_dialogs(
self,
offset_date: int = 0,
limit: int = 0
) -> Generator["pyrogram.Dialog", None, None]:
"""Use this method to iterate through a user's dialogs sequentially.
This convenience method does the same as repeatedly calling :meth:`get_dialogs` in a loop, thus saving you from
@@ -44,7 +46,7 @@ class IterDialogs(BaseClient):
A generator yielding :obj:`Dialog <pyrogram.Dialog>` objects.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
"""
current = 0
total = limit or (1 << 31) - 1

View File

@@ -16,13 +16,16 @@
# 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 pyrogram.api import functions, types
from ...ext import BaseClient
class JoinChat(BaseClient):
def join_chat(self,
chat_id: str):
def join_chat(
self,
chat_id: str
):
"""Use this method to join a group chat or channel.
Args:
@@ -30,17 +33,24 @@ class JoinChat(BaseClient):
Unique identifier for the target chat in form of a *t.me/joinchat/* link or username of the target
channel/supergroup (in the format @username).
Returns:
On success, a :obj:`Chat <pyrogram.Chat>` object is returned.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
"""
match = self.INVITE_LINK_RE.match(chat_id)
if match:
return self.send(
chat = self.send(
functions.messages.ImportChatInvite(
hash=match.group(1)
)
)
if isinstance(chat.chats[0], types.Chat):
return pyrogram.Chat._parse_chat_chat(self, chat.chats[0])
elif isinstance(chat.chats[0], types.Channel):
return pyrogram.Chat._parse_channel_chat(self, chat.chats[0])
else:
resolved_peer = self.send(
functions.contacts.ResolveUsername(
@@ -53,8 +63,10 @@ class JoinChat(BaseClient):
access_hash=resolved_peer.chats[0].access_hash
)
return self.send(
chat = self.send(
functions.channels.JoinChannel(
channel=channel
)
)
return pyrogram.Chat._parse_channel_chat(self, chat.chats[0])

View File

@@ -24,10 +24,12 @@ from ...ext import BaseClient
class KickChatMember(BaseClient):
def kick_chat_member(self,
chat_id: Union[int, str],
user_id: Union[int, str],
until_date: int = 0) -> Union["pyrogram.Message", bool]:
def kick_chat_member(
self,
chat_id: Union[int, str],
user_id: Union[int, str],
until_date: int = 0
) -> Union["pyrogram.Message", bool]:
"""Use this method to kick a user from a group, a supergroup or a channel.
In the case of supergroups and channels, the user will not be able to return to the group on their own using
invite links, etc., unless unbanned first. You must be an administrator in the chat for this to work and must
@@ -55,7 +57,7 @@ class KickChatMember(BaseClient):
On success, either True or a service :obj:`Message <pyrogram.Message>` will be returned (when applicable).
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
"""
chat_peer = self.resolve_peer(chat_id)
user_peer = self.resolve_peer(user_id)
@@ -65,7 +67,7 @@ class KickChatMember(BaseClient):
functions.channels.EditBanned(
channel=chat_peer,
user_id=user_peer,
banned_rights=types.ChannelBannedRights(
banned_rights=types.ChatBannedRights(
until_date=until_date,
view_messages=True,
send_messages=True,

View File

@@ -23,9 +23,11 @@ from ...ext import BaseClient
class LeaveChat(BaseClient):
def leave_chat(self,
chat_id: Union[int, str],
delete: bool = False):
def leave_chat(
self,
chat_id: Union[int, str],
delete: bool = False
):
"""Use this method to leave a group chat or channel.
Args:
@@ -37,7 +39,7 @@ class LeaveChat(BaseClient):
Deletes the group chat dialog after leaving (for simple group chats, not supergroups).
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
"""
peer = self.resolve_peer(chat_id)

View File

@@ -23,10 +23,12 @@ from ...ext import BaseClient
class PinChatMessage(BaseClient):
def pin_chat_message(self,
chat_id: Union[int, str],
message_id: int,
disable_notification: bool = None) -> bool:
def pin_chat_message(
self,
chat_id: Union[int, str],
message_id: int,
disable_notification: bool = None
) -> bool:
"""Use this method to pin a message in a group, channel or your own chat.
You must be an administrator in the chat for this to work and must have the "can_pin_messages" admin right in
the supergroup or "can_edit_messages" admin right in the channel.
@@ -46,7 +48,7 @@ class PinChatMessage(BaseClient):
True on success.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
"""
self.send(
functions.messages.UpdatePinnedMessage(
@@ -55,3 +57,5 @@ class PinChatMessage(BaseClient):
silent=disable_notification or None
)
)
return True

View File

@@ -23,18 +23,21 @@ from ...ext import BaseClient
class PromoteChatMember(BaseClient):
def promote_chat_member(self,
chat_id: Union[int, str],
user_id: Union[int, str],
can_change_info: bool = True,
can_post_messages: bool = False,
can_edit_messages: bool = False,
can_delete_messages: bool = True,
can_invite_users: bool = True,
can_restrict_members: bool = True,
can_pin_messages: bool = False,
can_promote_members: bool = False) -> bool:
def promote_chat_member(
self,
chat_id: Union[int, str],
user_id: Union[int, str],
can_change_info: bool = True,
can_post_messages: bool = False,
can_edit_messages: bool = False,
can_delete_messages: bool = True,
can_restrict_members: bool = True,
can_invite_users: bool = True,
can_pin_messages: bool = False,
can_promote_members: bool = False
) -> bool:
"""Use this method to promote or demote a user in a supergroup or a channel.
You must be an administrator in the chat for this to work and must have the appropriate admin rights.
Pass False for all boolean parameters to demote a user.
@@ -58,12 +61,12 @@ class PromoteChatMember(BaseClient):
can_delete_messages (``bool``, *optional*):
Pass True, if the administrator can delete messages of other users.
can_invite_users (``bool``, *optional*):
Pass True, if the administrator can invite new users to the chat.
can_restrict_members (``bool``, *optional*):
Pass True, if the administrator can restrict, ban or unban chat members.
can_invite_users (``bool``, *optional*):
Pass True, if the administrator can invite new users to the chat.
can_pin_messages (``bool``, *optional*):
Pass True, if the administrator can pin messages, supergroups only.
@@ -76,23 +79,21 @@ class PromoteChatMember(BaseClient):
True on success.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
"""
self.send(
functions.channels.EditAdmin(
channel=self.resolve_peer(chat_id),
user_id=self.resolve_peer(user_id),
admin_rights=types.ChannelAdminRights(
admin_rights=types.ChatAdminRights(
change_info=can_change_info or None,
post_messages=can_post_messages or None,
edit_messages=can_edit_messages or None,
delete_messages=can_delete_messages or None,
ban_users=can_restrict_members or None,
invite_users=can_invite_users or None,
invite_link=can_invite_users or None,
pin_messages=can_pin_messages or None,
add_admins=can_promote_members or None,
manage_call=None
)
)
)

View File

@@ -0,0 +1,143 @@
# 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 typing import Union
from pyrogram.api import functions, types
from ...ext import BaseClient
from ...types.user_and_chats import Chat
class RestrictChat(BaseClient):
def restrict_chat(
self,
chat_id: Union[int, str],
can_send_messages: bool = False,
can_send_media_messages: bool = False,
can_send_other_messages: bool = False,
can_add_web_page_previews: bool = False,
can_send_polls: bool = False,
can_change_info: bool = False,
can_invite_users: bool = False,
can_pin_messages: bool = False
) -> Chat:
"""Use this method to restrict a chat.
Pass True for all boolean parameters to lift restrictions from a chat.
Args:
chat_id (``int`` | ``str``):
Unique identifier (int) or username (str) of the target chat.
can_send_messages (``bool``, *optional*):
Pass True, if the user can send text messages, contacts, locations and venues.
can_send_media_messages (``bool``, *optional*):
Pass True, if the user can send audios, documents, photos, videos, video notes and voice notes,
implies can_send_messages.
can_send_other_messages (``bool``, *optional*):
Pass True, if the user can send animations, games, stickers and use inline bots,
implies can_send_media_messages.
can_add_web_page_previews (``bool``, *optional*):
Pass True, if the user may add web page previews to their messages, implies can_send_media_messages.
can_send_polls (``bool``, *optional*):
Pass True, if the user can send polls, implies can_send_media_messages.
can_change_info (``bool``, *optional*):
Pass True, if the user can change the chat title, photo and other settings.
can_invite_users (``bool``, *optional*):
Pass True, if the user can invite new users to the chat.
can_pin_messages (``bool``, *optional*):
Pass True, if the user can pin messages.
Returns:
On success, a :obj:`Chat <pyrogram.Chat>` object is returned.
Raises:
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
"""
send_messages = True
send_media = True
send_stickers = True
send_gifs = True
send_games = True
send_inline = True
embed_links = True
send_polls = True
change_info = True
invite_users = True
pin_messages = True
if can_send_messages:
send_messages = None
if can_send_media_messages:
send_messages = None
send_media = None
if can_send_other_messages:
send_messages = None
send_media = None
send_stickers = None
send_gifs = None
send_games = None
send_inline = None
if can_add_web_page_previews:
send_messages = None
send_media = None
embed_links = None
if can_send_polls:
send_messages = None
send_polls = None
if can_change_info:
change_info = None
if can_invite_users:
invite_users = None
if can_pin_messages:
pin_messages = None
r = self.send(
functions.messages.EditChatDefaultBannedRights(
peer=self.resolve_peer(chat_id),
banned_rights=types.ChatBannedRights(
until_date=0,
send_messages=send_messages,
send_media=send_media,
send_stickers=send_stickers,
send_gifs=send_gifs,
send_games=send_games,
send_inline=send_inline,
embed_links=embed_links,
send_polls=send_polls,
change_info=change_info,
invite_users=invite_users,
pin_messages=pin_messages
)
)
)
return Chat._parse_chat(self, r.chats[0])

View File

@@ -20,20 +20,28 @@ from typing import Union
from pyrogram.api import functions, types
from ...ext import BaseClient
from ...types.user_and_chats import Chat
class RestrictChatMember(BaseClient):
def restrict_chat_member(self,
chat_id: Union[int, str],
user_id: Union[int, str],
until_date: int = 0,
can_send_messages: bool = False,
can_send_media_messages: bool = False,
can_send_other_messages: bool = False,
can_add_web_page_previews: bool = False) -> bool:
"""Use this method to restrict a user in a supergroup. The bot must be an administrator in the supergroup for
this to work and must have the appropriate admin rights. Pass True for all boolean parameters to lift
restrictions from a user.
def restrict_chat_member(
self,
chat_id: Union[int, str],
user_id: Union[int, str],
until_date: int = 0,
can_send_messages: bool = False,
can_send_media_messages: bool = False,
can_send_other_messages: bool = False,
can_add_web_page_previews: bool = False,
can_send_polls: bool = False,
can_change_info: bool = False,
can_invite_users: bool = False,
can_pin_messages: bool = False
) -> Chat:
"""Use this method to restrict a user in a supergroup.
The bot must be an administrator in the supergroup for this to work and must have the appropriate admin rights.
Pass True for all boolean parameters to lift restrictions from a user.
Args:
chat_id (``int`` | ``str``):
@@ -60,13 +68,25 @@ class RestrictChatMember(BaseClient):
implies can_send_media_messages.
can_add_web_page_previews (``bool``, *optional*):
Pass True, if the user may add web page previews to their messages, implies can_send_media_messages
Pass True, if the user may add web page previews to their messages, implies can_send_media_messages.
can_send_polls (``bool``, *optional*):
Pass True, if the user can send polls, implies can_send_media_messages.
can_change_info (``bool``, *optional*):
Pass True, if the user can change the chat title, photo and other settings.
can_invite_users (``bool``, *optional*):
Pass True, if the user can invite new users to the chat.
can_pin_messages (``bool``, *optional*):
Pass True, if the user can pin messages.
Returns:
True on success.
On success, a :obj:`Chat <pyrogram.Chat>` object is returned.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
"""
send_messages = True
send_media = True
@@ -75,6 +95,10 @@ class RestrictChatMember(BaseClient):
send_games = True
send_inline = True
embed_links = True
send_polls = True
change_info = True
invite_users = True
pin_messages = True
if can_send_messages:
send_messages = None
@@ -84,6 +108,7 @@ class RestrictChatMember(BaseClient):
send_media = None
if can_send_other_messages:
send_messages = None
send_media = None
send_stickers = None
send_gifs = None
@@ -91,14 +116,28 @@ class RestrictChatMember(BaseClient):
send_inline = None
if can_add_web_page_previews:
send_messages = None
send_media = None
embed_links = None
self.send(
if can_send_polls:
send_messages = None
send_polls = None
if can_change_info:
change_info = None
if can_invite_users:
invite_users = None
if can_pin_messages:
pin_messages = None
r = self.send(
functions.channels.EditBanned(
channel=self.resolve_peer(chat_id),
user_id=self.resolve_peer(user_id),
banned_rights=types.ChannelBannedRights(
banned_rights=types.ChatBannedRights(
until_date=until_date,
send_messages=send_messages,
send_media=send_media,
@@ -106,9 +145,13 @@ class RestrictChatMember(BaseClient):
send_gifs=send_gifs,
send_games=send_games,
send_inline=send_inline,
embed_links=embed_links
embed_links=embed_links,
send_polls=send_polls,
change_info=change_info,
invite_users=invite_users,
pin_messages=pin_messages
)
)
)
return True
return Chat._parse_chat(self, r.chats[0])

View File

@@ -23,9 +23,11 @@ from ...ext import BaseClient
class SetChatDescription(BaseClient):
def set_chat_description(self,
chat_id: Union[int, str],
description: str) -> bool:
def set_chat_description(
self,
chat_id: Union[int, str],
description: str
) -> bool:
"""Use this method to change the description of a supergroup or a channel.
You must be an administrator in the chat for this to work and must have the appropriate admin rights.
@@ -40,20 +42,18 @@ class SetChatDescription(BaseClient):
True on success.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
``ValueError`` if a chat_id doesn't belong to a supergroup or a channel.
"""
peer = self.resolve_peer(chat_id)
if isinstance(peer, types.InputPeerChannel):
if isinstance(peer, (types.InputPeerChannel, types.InputPeerChat)):
self.send(
functions.channels.EditAbout(
channel=peer,
functions.messages.EditChatAbout(
peer=peer,
about=description
)
)
elif isinstance(peer, types.InputPeerChat):
raise ValueError("The chat_id \"{}\" belongs to a basic group".format(chat_id))
else:
raise ValueError("The chat_id \"{}\" belongs to a user".format(chat_id))

View File

@@ -26,9 +26,11 @@ from ...ext import BaseClient
class SetChatPhoto(BaseClient):
def set_chat_photo(self,
chat_id: Union[int, str],
photo: str) -> bool:
def set_chat_photo(
self,
chat_id: Union[int, str],
photo: str
) -> bool:
"""Use this method to set a new profile photo for the chat.
Photos can't be changed for private chats.
You must be an administrator in the chat for this to work and must have the appropriate admin rights.
@@ -48,7 +50,7 @@ class SetChatPhoto(BaseClient):
True on success.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
``ValueError`` if a chat_id belongs to user.
"""
peer = self.resolve_peer(chat_id)

View File

@@ -23,9 +23,11 @@ from ...ext import BaseClient
class SetChatTitle(BaseClient):
def set_chat_title(self,
chat_id: Union[int, str],
title: str) -> bool:
def set_chat_title(
self,
chat_id: Union[int, str],
title: str
) -> bool:
"""Use this method to change the title of a chat.
Titles can't be changed for private chats.
You must be an administrator in the chat for this to work and must have the appropriate admin rights.
@@ -45,7 +47,7 @@ class SetChatTitle(BaseClient):
True on success.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
``ValueError`` if a chat_id belongs to user.
"""
peer = self.resolve_peer(chat_id)

View File

@@ -23,9 +23,11 @@ from ...ext import BaseClient
class UnbanChatMember(BaseClient):
def unban_chat_member(self,
chat_id: Union[int, str],
user_id: Union[int, str]) -> bool:
def unban_chat_member(
self,
chat_id: Union[int, str],
user_id: Union[int, str]
) -> bool:
"""Use this method to unban a previously kicked user in a supergroup or channel.
The user will **not** return to the group or channel automatically, but will be able to join via link, etc.
You must be an administrator for this to work.
@@ -42,13 +44,13 @@ class UnbanChatMember(BaseClient):
True on success.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
"""
self.send(
functions.channels.EditBanned(
channel=self.resolve_peer(chat_id),
user_id=self.resolve_peer(user_id),
banned_rights=types.ChannelBannedRights(
banned_rights=types.ChatBannedRights(
until_date=0
)
)

View File

@@ -23,8 +23,10 @@ from ...ext import BaseClient
class UnpinChatMessage(BaseClient):
def unpin_chat_message(self,
chat_id: Union[int, str]) -> bool:
def unpin_chat_message(
self,
chat_id: Union[int, str]
) -> bool:
"""Use this method to unpin a message in a group, channel or your own chat.
You must be an administrator in the chat for this to work and must have the "can_pin_messages" admin
right in the supergroup or "can_edit_messages" admin right in the channel.
@@ -37,7 +39,7 @@ class UnpinChatMessage(BaseClient):
True on success.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
"""
self.send(
functions.messages.UpdatePinnedMessage(

View File

@@ -0,0 +1,61 @@
# 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 typing import Union
from pyrogram.api import functions, types
from ...ext import BaseClient
class UpdateChatUsername(BaseClient):
def update_chat_username(
self,
chat_id: Union[int, str],
username: Union[str, None]
) -> bool:
"""Use this method to update a channel or a supergroup username.
To update your own username (for users only, not bots) you can use :meth:`update_username`.
Args:
chat_id (``int`` | ``str``)
Unique identifier (int) or username (str) of the target chat.
username (``str`` | ``None``):
Username to set. Pass "" (empty string) or None to remove the username.
Returns:
True on success.
Raises:
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
``ValueError`` if a chat_id belongs to a user or chat.
"""
peer = self.resolve_peer(chat_id)
if isinstance(peer, types.InputPeerChannel):
return bool(
self.send(
functions.channels.UpdateUsername(
channel=peer,
username=username or ""
)
)
)
else:
raise ValueError("The chat_id \"{}\" belongs to a user or chat".format(chat_id))

View File

@@ -24,8 +24,10 @@ from ...ext import BaseClient
class AddContacts(BaseClient):
def add_contacts(self,
contacts: List["pyrogram.InputPhoneContact"]):
def add_contacts(
self,
contacts: List["pyrogram.InputPhoneContact"]
):
"""Use this method to add contacts to your Telegram address book.
Args:
@@ -36,7 +38,7 @@ class AddContacts(BaseClient):
On success, the added contacts are returned.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
"""
imported_contacts = self.send(
functions.contacts.ImportContacts(

View File

@@ -19,14 +19,16 @@
from typing import List
from pyrogram.api import functions, types
from pyrogram.api.errors import PeerIdInvalid
from pyrogram.errors import PeerIdInvalid
from ...ext import BaseClient
class DeleteContacts(BaseClient):
def delete_contacts(self,
ids: List[int]):
"""Use this method to delete contacts from your Telegram address book
def delete_contacts(
self,
ids: List[int]
):
"""Use this method to delete contacts from your Telegram address book.
Args:
ids (List of ``int``):
@@ -37,7 +39,7 @@ class DeleteContacts(BaseClient):
True on success.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
"""
contacts = []

View File

@@ -21,7 +21,7 @@ import time
import pyrogram
from pyrogram.api import functions
from pyrogram.api.errors import FloodWait
from pyrogram.errors import FloodWait
from ...ext import BaseClient
log = logging.getLogger(__name__)
@@ -35,11 +35,11 @@ class GetContacts(BaseClient):
On success, a list of :obj:`User` objects is returned.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
"""
while True:
try:
contacts = self.send(functions.contacts.GetContacts(0))
contacts = self.send(functions.contacts.GetContacts(hash=0))
except FloodWait as e:
log.warning("get_contacts flood: waiting {} seconds".format(e.x))
time.sleep(e.x)

View File

@@ -19,6 +19,7 @@
from .on_callback_query import OnCallbackQuery
from .on_deleted_messages import OnDeletedMessages
from .on_disconnect import OnDisconnect
from .on_inline_query import OnInlineQuery
from .on_message import OnMessage
from .on_raw_update import OnRawUpdate
from .on_user_status import OnUserStatus
@@ -30,6 +31,7 @@ class Decorators(
OnCallbackQuery,
OnRawUpdate,
OnDisconnect,
OnUserStatus
OnUserStatus,
OnInlineQuery
):
pass

View File

@@ -25,19 +25,13 @@ from ...ext import BaseClient
class OnCallbackQuery(BaseClient):
def on_callback_query(self=None,
filters=None,
group: int = 0) -> callable:
"""Use this decorator to automatically register a function for handling
callback queries. This does the same thing as :meth:`add_handler` using the
:class:`CallbackQueryHandler`.
.. note::
This decorator will wrap your defined function in a tuple consisting of *(Handler, group)*.
To reference your own function after it has been decorated, you need to access
*my_function[0].callback*, that is, the *callback* field of Handler object which is the the
first element in the tuple.
def on_callback_query(
self=None,
filters=None,
group: int = 0
) -> callable:
"""Use this decorator to automatically register a function for handling callback queries.
This does the same thing as :meth:`add_handler` using the :class:`CallbackQueryHandler`.
Args:
filters (:obj:`Filters <pyrogram.Filters>`):

View File

@@ -25,19 +25,13 @@ from ...ext import BaseClient
class OnDeletedMessages(BaseClient):
def on_deleted_messages(self=None,
filters=None,
group: int = 0) -> callable:
"""Use this decorator to automatically register a function for handling
deleted messages. This does the same thing as :meth:`add_handler` using the
:class:`DeletedMessagesHandler`.
.. note::
This decorator will wrap your defined function in a tuple consisting of *(Handler, group)*.
To reference your own function after it has been decorated, you need to access
*my_function[0].callback*, that is, the *callback* field of Handler object which is the the
first element in the tuple.
def on_deleted_messages(
self=None,
filters=None,
group: int = 0
) -> callable:
"""Use this decorator to automatically register a function for handling deleted messages.
This does the same thing as :meth:`add_handler` using the :class:`DeletedMessagesHandler`.
Args:
filters (:obj:`Filters <pyrogram.Filters>`):

View File

@@ -23,9 +23,8 @@ from ...ext import BaseClient
class OnDisconnect(BaseClient):
def on_disconnect(self=None) -> callable:
"""Use this decorator to automatically register a function for handling
disconnections. This does the same thing as :meth:`add_handler` using the
:class:`DisconnectHandler`.
"""Use this decorator to automatically register a function for handling disconnections.
This does the same thing as :meth:`add_handler` using the :class:`DisconnectHandler`.
"""
def decorator(func: callable) -> Handler:

View File

@@ -0,0 +1,59 @@
# 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 typing import Tuple
import pyrogram
from pyrogram.client.filters.filter import Filter
from pyrogram.client.handlers.handler import Handler
from ...ext import BaseClient
class OnInlineQuery(BaseClient):
def on_inline_query(
self=None,
filters=None,
group: int = 0
) -> callable:
"""Use this decorator to automatically register a function for handling inline queries.
This does the same thing as :meth:`add_handler` using the :class:`InlineQueryHandler`.
Args:
filters (:obj:`Filters <pyrogram.Filters>`):
Pass one or more filters to allow only a subset of inline queries to be passed
in your function.
group (``int``, *optional*):
The group identifier, defaults to 0.
"""
def decorator(func: callable) -> Tuple[Handler, int]:
if isinstance(func, tuple):
func = func[0].callback
handler = pyrogram.InlineQueryHandler(func, filters)
if isinstance(self, Filter):
return pyrogram.InlineQueryHandler(func, self), group if filters is None else filters
if self is not None:
self.add_handler(handler, group)
return handler, group
return decorator

View File

@@ -25,19 +25,13 @@ from ...ext import BaseClient
class OnMessage(BaseClient):
def on_message(self=None,
filters=None,
group: int = 0) -> callable:
"""Use this decorator to automatically register a function for handling
messages. This does the same thing as :meth:`add_handler` using the
:class:`MessageHandler`.
.. note::
This decorator will wrap your defined function in a tuple consisting of *(Handler, group)*.
To reference your own function after it has been decorated, you need to access
*my_function[0].callback*, that is, the *callback* field of Handler object which is the the
first element in the tuple.
def on_message(
self=None,
filters=None,
group: int = 0
) -> callable:
"""Use this decorator to automatically register a function for handling messages.
This does the same thing as :meth:`add_handler` using the :class:`MessageHandler`.
Args:
filters (:obj:`Filters <pyrogram.Filters>`):

View File

@@ -24,18 +24,12 @@ from ...ext import BaseClient
class OnRawUpdate(BaseClient):
def on_raw_update(self=None,
group: int = 0) -> callable:
"""Use this decorator to automatically register a function for handling
raw updates. This does the same thing as :meth:`add_handler` using the
:class:`RawUpdateHandler`.
.. note::
This decorator will wrap your defined function in a tuple consisting of *(Handler, group)*.
To reference your own function after it has been decorated, you need to access
*my_function[0].callback*, that is, the *callback* field of Handler object which is the the
first element in the tuple.
def on_raw_update(
self=None,
group: int = 0
) -> callable:
"""Use this decorator to automatically register a function for handling raw updates.
This does the same thing as :meth:`add_handler` using the :class:`RawUpdateHandler`.
Args:
group (``int``, *optional*):

View File

@@ -25,19 +25,13 @@ from ...ext import BaseClient
class OnUserStatus(BaseClient):
def on_user_status(self=None,
filters=None,
group: int = 0) -> callable:
"""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`.
.. note::
This decorator will wrap your defined function in a tuple consisting of *(Handler, group)*.
To reference your own function after it has been decorated, you need to access
*my_function[0].callback*, that is, the *callback* field of Handler object which is the the
first element in the tuple.
def on_user_status(
self=None,
filters=None,
group: int = 0
) -> callable:
"""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>`):

View File

@@ -30,6 +30,7 @@ from .iter_history import IterHistory
from .retract_vote import RetractVote
from .send_animation import SendAnimation
from .send_audio import SendAudio
from .send_cached_media import SendCachedMedia
from .send_chat_action import SendChatAction
from .send_contact import SendContact
from .send_document import SendDocument
@@ -74,6 +75,7 @@ class Messages(
ClosePoll,
RetractVote,
DownloadMedia,
IterHistory
IterHistory,
SendCachedMedia
):
pass

View File

@@ -23,9 +23,11 @@ from pyrogram.client.ext import BaseClient
class ClosePoll(BaseClient):
def close_poll(self,
chat_id: Union[int, str],
message_id: id) -> bool:
def close_poll(
self,
chat_id: Union[int, str],
message_id: id
) -> bool:
"""Use this method to close (stop) a poll.
Closed polls can't be reopened and nobody will be able to vote in it anymore.
@@ -43,7 +45,7 @@ class ClosePoll(BaseClient):
On success, True is returned.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
"""
poll = self.get_messages(chat_id, message_id).poll

View File

@@ -23,17 +23,13 @@ from pyrogram.client.ext import BaseClient
class DeleteMessages(BaseClient):
def delete_messages(self,
chat_id: Union[int, str],
message_ids: Iterable[int],
revoke: bool = True) -> bool:
"""Use this method to delete messages, including service messages, with the following limitations:
- A message can only be deleted if it was sent less than 48 hours ago.
- Users can delete outgoing messages in groups and supergroups.
- Users granted *can_post_messages* permissions can delete outgoing messages in channels.
- If the user is an administrator of a group, it can delete any message there.
- If the user has *can_delete_messages* permission in a supergroup or a channel, it can delete any message there.
def delete_messages(
self,
chat_id: Union[int, str],
message_ids: Iterable[int],
revoke: bool = True
) -> bool:
"""Use this method to delete messages, including service messages.
Args:
chat_id (``int`` | ``str``):
@@ -55,7 +51,7 @@ class DeleteMessages(BaseClient):
True on success.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
"""
peer = self.resolve_peer(chat_id)
message_ids = list(message_ids) if not isinstance(message_ids, int) else [message_ids]

View File

@@ -24,13 +24,15 @@ from pyrogram.client.ext import BaseClient
class DownloadMedia(BaseClient):
def download_media(self,
message: Union["pyrogram.Message", str],
file_name: str = "",
block: bool = True,
progress: callable = None,
progress_args: tuple = ()) -> Union[str, None]:
"""Use this method to download the media from a Message.
def download_media(
self,
message: Union["pyrogram.Message", str],
file_name: str = "",
block: bool = True,
progress: callable = None,
progress_args: tuple = ()
) -> Union[str, None]:
"""Use this method to download the media from a message.
Args:
message (:obj:`Message <pyrogram.Message>` | ``str``):
@@ -75,7 +77,7 @@ class DownloadMedia(BaseClient):
In case the download is deliberately stopped with :meth:`stop_transmission`, None is returned as well.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
``ValueError`` if the message doesn't contain any downloadable media
"""
error_message = "This message doesn't contain any downloadable media"
@@ -106,15 +108,15 @@ class DownloadMedia(BaseClient):
else:
raise ValueError(error_message)
elif isinstance(message, (
pyrogram.Photo,
pyrogram.PhotoSize,
pyrogram.Audio,
pyrogram.Document,
pyrogram.Video,
pyrogram.Voice,
pyrogram.VideoNote,
pyrogram.Sticker,
pyrogram.Animation
pyrogram.Photo,
pyrogram.PhotoSize,
pyrogram.Audio,
pyrogram.Document,
pyrogram.Video,
pyrogram.Voice,
pyrogram.VideoNote,
pyrogram.Sticker,
pyrogram.Animation
)):
if isinstance(message, pyrogram.Photo):
media = pyrogram.Document(

View File

@@ -24,12 +24,14 @@ from pyrogram.client.ext import BaseClient
class EditMessageCaption(BaseClient):
def edit_message_caption(self,
chat_id: Union[int, str],
message_id: int,
caption: str,
parse_mode: str = "",
reply_markup: "pyrogram.InlineKeyboardMarkup" = None) -> "pyrogram.Message":
def edit_message_caption(
self,
chat_id: Union[int, str],
message_id: int,
caption: str,
parse_mode: str = "",
reply_markup: "pyrogram.InlineKeyboardMarkup" = None
) -> "pyrogram.Message":
"""Use this method to edit captions of messages.
Args:
@@ -56,7 +58,7 @@ class EditMessageCaption(BaseClient):
On success, the edited :obj:`Message <pyrogram.Message>` is returned.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
"""
style = self.html if parse_mode.lower() == "html" else self.markdown

View File

@@ -23,7 +23,7 @@ from typing import Union
import pyrogram
from pyrogram.api import functions, types
from pyrogram.api.errors import FileIdInvalid
from pyrogram.errors import FileIdInvalid
from pyrogram.client.ext import BaseClient, utils
from pyrogram.client.types import (
InputMediaPhoto, InputMediaVideo, InputMediaAudio,
@@ -33,11 +33,13 @@ from pyrogram.client.types.input_media import InputMedia
class EditMessageMedia(BaseClient):
def edit_message_media(self,
chat_id: Union[int, str],
message_id: int,
media: InputMedia,
reply_markup: "pyrogram.InlineKeyboardMarkup" = None) -> "pyrogram.Message":
def edit_message_media(
self,
chat_id: Union[int, str],
message_id: int,
media: InputMedia,
reply_markup: "pyrogram.InlineKeyboardMarkup" = None
) -> "pyrogram.Message":
"""Use this method to edit audio, document, photo, or video messages.
If a message is a part of a message album, then it can be edited only to a photo or a video. Otherwise,
@@ -54,7 +56,7 @@ class EditMessageMedia(BaseClient):
message_id (``int``):
Message identifier in the chat specified in chat_id.
media (:obj:`InputMediaAnimation` | :obj:`InputMediaAudio` | :obj:`InputMediaDocument` | :obj:`InputMediaPhoto` | :obj:`InputMediaVideo`)
media (:obj:`InputMedia`)
One of the InputMedia objects describing an animation, audio, document, photo or video.
reply_markup (:obj:`InlineKeyboardMarkup`, *optional*):
@@ -64,7 +66,7 @@ class EditMessageMedia(BaseClient):
On success, the edited :obj:`Message <pyrogram.Message>` is returned.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
"""
style = self.html if media.parse_mode.lower() == "html" else self.markdown
caption = media.caption
@@ -131,7 +133,9 @@ class EditMessageMedia(BaseClient):
w=media.width,
h=media.height
),
types.DocumentAttributeFilename(os.path.basename(media.media))
types.DocumentAttributeFilename(
file_name=os.path.basename(media.media)
)
]
)
)
@@ -187,7 +191,9 @@ class EditMessageMedia(BaseClient):
performer=media.performer,
title=media.title
),
types.DocumentAttributeFilename(os.path.basename(media.media))
types.DocumentAttributeFilename(
file_name=os.path.basename(media.media)
)
]
)
)
@@ -244,7 +250,9 @@ class EditMessageMedia(BaseClient):
w=media.width,
h=media.height
),
types.DocumentAttributeFilename(os.path.basename(media.media)),
types.DocumentAttributeFilename(
file_name=os.path.basename(media.media)
),
types.DocumentAttributeAnimated()
]
)
@@ -296,7 +304,9 @@ class EditMessageMedia(BaseClient):
thumb=None if media.thumb is None else self.save_file(media.thumb),
file=self.save_file(media.media),
attributes=[
types.DocumentAttributeFilename(os.path.basename(media.media))
types.DocumentAttributeFilename(
file_name=os.path.basename(media.media)
)
]
)
)

View File

@@ -24,10 +24,12 @@ from pyrogram.client.ext import BaseClient
class EditMessageReplyMarkup(BaseClient):
def edit_message_reply_markup(self,
chat_id: Union[int, str],
message_id: int,
reply_markup: "pyrogram.InlineKeyboardMarkup" = None) -> "pyrogram.Message":
def edit_message_reply_markup(
self,
chat_id: Union[int, str],
message_id: int,
reply_markup: "pyrogram.InlineKeyboardMarkup" = None
) -> "pyrogram.Message":
"""Use this method to edit only the reply markup of messages sent by the bot or via the bot (for inline bots).
Args:
@@ -47,7 +49,7 @@ class EditMessageReplyMarkup(BaseClient):
:obj:`Message <pyrogram.Message>` is returned, otherwise True is returned.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
"""
r = self.send(

View File

@@ -24,13 +24,15 @@ from pyrogram.client.ext import BaseClient
class EditMessageText(BaseClient):
def edit_message_text(self,
chat_id: Union[int, str],
message_id: int,
text: str,
parse_mode: str = "",
disable_web_page_preview: bool = None,
reply_markup: "pyrogram.InlineKeyboardMarkup" = None) -> "pyrogram.Message":
def edit_message_text(
self,
chat_id: Union[int, str],
message_id: int,
text: str,
parse_mode: str = "",
disable_web_page_preview: bool = None,
reply_markup: "pyrogram.InlineKeyboardMarkup" = None
) -> "pyrogram.Message":
"""Use this method to edit text messages.
Args:
@@ -60,7 +62,7 @@ class EditMessageText(BaseClient):
On success, the edited :obj:`Message <pyrogram.Message>` is returned.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
"""
style = self.html if parse_mode.lower() == "html" else self.markdown

View File

@@ -24,11 +24,15 @@ from ...ext import BaseClient
class ForwardMessages(BaseClient):
def forward_messages(self,
chat_id: Union[int, str],
from_chat_id: Union[int, str],
message_ids: Iterable[int],
disable_notification: bool = None) -> "pyrogram.Messages":
def forward_messages(
self,
chat_id: Union[int, str],
from_chat_id: Union[int, str],
message_ids: Iterable[int],
disable_notification: bool = None,
as_copy: bool = False,
remove_caption: bool = False
) -> "pyrogram.Messages":
"""Use this method to forward messages of any kind.
Args:
@@ -50,6 +54,15 @@ class ForwardMessages(BaseClient):
Sends the message silently.
Users will receive a notification with no sound.
as_copy (``bool``, *optional*):
Pass True to forward messages without the forward header (i.e.: send a copy of the message content).
Defaults to False.
remove_caption (``bool``, *optional*):
If set to True and *as_copy* is enabled as well, media captions are not preserved when copying the
message. Has no effect if *as_copy* is not enabled.
Defaults to False.
Returns:
On success and in case *message_ids* was an iterable, the returned value will be a list of the forwarded
:obj:`Messages <pyrogram.Message>` even if a list contains just one element, otherwise if
@@ -57,37 +70,60 @@ class ForwardMessages(BaseClient):
is returned.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
"""
is_iterable = not isinstance(message_ids, int)
message_ids = list(message_ids) if is_iterable else [message_ids]
r = self.send(
functions.messages.ForwardMessages(
to_peer=self.resolve_peer(chat_id),
from_peer=self.resolve_peer(from_chat_id),
id=message_ids,
silent=disable_notification or None,
random_id=[self.rnd_id() for _ in message_ids]
)
)
if as_copy:
forwarded_messages = []
messages = []
for chunk in [message_ids[i:i + 200] for i in range(0, len(message_ids), 200)]:
messages = self.get_messages(chat_id=from_chat_id, message_ids=chunk) # type: pyrogram.Messages
users = {i.id: i for i in r.users}
chats = {i.id: i for i in r.chats}
for i in r.updates:
if isinstance(i, (types.UpdateNewMessage, types.UpdateNewChannelMessage)):
messages.append(
pyrogram.Message._parse(
self, i.message,
users, chats
for message in messages.messages:
forwarded_messages.append(
message.forward(
chat_id,
disable_notification=disable_notification,
as_copy=True,
remove_caption=remove_caption
)
)
)
return pyrogram.Messages(
client=self,
total_count=len(messages),
messages=messages
) if is_iterable else messages[0]
return pyrogram.Messages(
client=self,
total_count=len(forwarded_messages),
messages=forwarded_messages
) if is_iterable else forwarded_messages[0]
else:
r = self.send(
functions.messages.ForwardMessages(
to_peer=self.resolve_peer(chat_id),
from_peer=self.resolve_peer(from_chat_id),
id=message_ids,
silent=disable_notification or None,
random_id=[self.rnd_id() for _ in message_ids]
)
)
forwarded_messages = []
users = {i.id: i for i in r.users}
chats = {i.id: i for i in r.chats}
for i in r.updates:
if isinstance(i, (types.UpdateNewMessage, types.UpdateNewChannelMessage)):
forwarded_messages.append(
pyrogram.Message._parse(
self, i.message,
users, chats
)
)
return pyrogram.Messages(
client=self,
total_count=len(forwarded_messages),
messages=forwarded_messages
) if is_iterable else forwarded_messages[0]

View File

@@ -22,20 +22,22 @@ from typing import Union
import pyrogram
from pyrogram.api import functions
from pyrogram.api.errors import FloodWait
from pyrogram.errors import FloodWait
from ...ext import BaseClient
log = logging.getLogger(__name__)
class GetHistory(BaseClient):
def get_history(self,
chat_id: Union[int, str],
limit: int = 100,
offset: int = 0,
offset_id: int = 0,
offset_date: int = 0,
reverse: bool = False):
def get_history(
self,
chat_id: Union[int, str],
limit: int = 100,
offset: int = 0,
offset_id: int = 0,
offset_date: int = 0,
reverse: bool = False
):
"""Use this method to retrieve a chunk of the history of a chat.
You can get up to 100 messages at once.
@@ -68,7 +70,7 @@ class GetHistory(BaseClient):
On success, a :obj:`Messages <pyrogram.Messages>` object is returned.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
"""
while True:

View File

@@ -22,18 +22,20 @@ from typing import Union, Iterable
import pyrogram
from pyrogram.api import functions, types
from pyrogram.api.errors import FloodWait
from pyrogram.errors import FloodWait
from ...ext import BaseClient
log = logging.getLogger(__name__)
class GetMessages(BaseClient):
def get_messages(self,
chat_id: Union[int, str],
message_ids: Union[int, Iterable[int]] = None,
reply_to_message_ids: Union[int, Iterable[int]] = None,
replies: int = 1) -> Union["pyrogram.Message", "pyrogram.Messages"]:
def get_messages(
self,
chat_id: Union[int, str],
message_ids: Union[int, Iterable[int]] = None,
reply_to_message_ids: Union[int, Iterable[int]] = None,
replies: int = 1
) -> Union["pyrogram.Message", "pyrogram.Messages"]:
"""Use this method to get one or more messages that belong to a specific chat.
You can retrieve up to 200 messages at once.
@@ -61,7 +63,7 @@ class GetMessages(BaseClient):
*reply_to_message_ids* was an integer, the single requested :obj:`Message <pyrogram.Message>` is returned.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
"""
ids, ids_type = (
(message_ids, types.InputMessageID) if message_ids
@@ -76,7 +78,7 @@ class GetMessages(BaseClient):
is_iterable = not isinstance(ids, int)
ids = list(ids) if is_iterable else [ids]
ids = [ids_type(i) for i in ids]
ids = [ids_type(id=i) for i in ids]
if isinstance(peer, types.InputPeerChannel):
rpc = functions.channels.GetMessages(channel=peer, id=ids)

View File

@@ -23,13 +23,15 @@ from ...ext import BaseClient
class IterHistory(BaseClient):
def iter_history(self,
chat_id: Union[int, str],
limit: int = 0,
offset: int = 0,
offset_id: int = 0,
offset_date: int = 0,
reverse: bool = False) -> Generator["pyrogram.Message", None, None]:
def iter_history(
self,
chat_id: Union[int, str],
limit: int = 0,
offset: int = 0,
offset_id: int = 0,
offset_date: int = 0,
reverse: bool = False
) -> Generator["pyrogram.Message", None, None]:
"""Use this method to iterate through a chat history sequentially.
This convenience method does the same as repeatedly calling :meth:`get_history` in a loop, thus saving you from
@@ -62,7 +64,7 @@ class IterHistory(BaseClient):
A generator yielding :obj:`Message <pyrogram.Message>` objects.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
"""
offset_id = offset_id or (1 if reverse else 0)
current = 0

View File

@@ -23,9 +23,11 @@ from pyrogram.client.ext import BaseClient
class RetractVote(BaseClient):
def retract_vote(self,
chat_id: Union[int, str],
message_id: id) -> bool:
def retract_vote(
self,
chat_id: Union[int, str],
message_id: id
) -> bool:
"""Use this method to retract your vote in a poll.
Args:
@@ -41,7 +43,7 @@ class RetractVote(BaseClient):
On success, True is returned.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
"""
self.send(
functions.messages.SendVote(

View File

@@ -23,28 +23,32 @@ from typing import Union
import pyrogram
from pyrogram.api import functions, types
from pyrogram.api.errors import FileIdInvalid, FilePartMissing
from pyrogram.errors import FileIdInvalid, FilePartMissing
from pyrogram.client.ext import BaseClient, utils
class SendAnimation(BaseClient):
def send_animation(self,
chat_id: Union[int, str],
animation: str,
caption: str = "",
parse_mode: str = "",
duration: int = 0,
width: int = 0,
height: int = 0,
thumb: str = None,
disable_notification: bool = None,
reply_to_message_id: int = None,
reply_markup: Union["pyrogram.InlineKeyboardMarkup",
"pyrogram.ReplyKeyboardMarkup",
"pyrogram.ReplyKeyboardRemove",
"pyrogram.ForceReply"] = None,
progress: callable = None,
progress_args: tuple = ()) -> Union["pyrogram.Message", None]:
def send_animation(
self,
chat_id: Union[int, str],
animation: str,
caption: str = "",
parse_mode: str = "",
duration: int = 0,
width: int = 0,
height: int = 0,
thumb: str = None,
disable_notification: bool = None,
reply_to_message_id: int = None,
reply_markup: Union[
"pyrogram.InlineKeyboardMarkup",
"pyrogram.ReplyKeyboardMarkup",
"pyrogram.ReplyKeyboardRemove",
"pyrogram.ForceReply"
] = None,
progress: callable = None,
progress_args: tuple = ()
) -> Union["pyrogram.Message", None]:
"""Use this method to send animation files (animation or H.264/MPEG-4 AVC video without sound).
Args:
@@ -121,7 +125,7 @@ class SendAnimation(BaseClient):
In case the upload is deliberately stopped with :meth:`stop_transmission`, None is returned instead.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
"""
file = None
style = self.html if parse_mode.lower() == "html" else self.markdown
@@ -141,7 +145,7 @@ class SendAnimation(BaseClient):
w=width,
h=height
),
types.DocumentAttributeFilename(os.path.basename(animation)),
types.DocumentAttributeFilename(file_name=os.path.basename(animation)),
types.DocumentAttributeAnimated()
]
)

View File

@@ -23,28 +23,32 @@ from typing import Union
import pyrogram
from pyrogram.api import functions, types
from pyrogram.api.errors import FileIdInvalid, FilePartMissing
from pyrogram.errors import FileIdInvalid, FilePartMissing
from pyrogram.client.ext import BaseClient, utils
class SendAudio(BaseClient):
def send_audio(self,
chat_id: Union[int, str],
audio: str,
caption: str = "",
parse_mode: str = "",
duration: int = 0,
performer: str = None,
title: str = None,
thumb: str = None,
disable_notification: bool = None,
reply_to_message_id: int = None,
reply_markup: Union["pyrogram.InlineKeyboardMarkup",
"pyrogram.ReplyKeyboardMarkup",
"pyrogram.ReplyKeyboardRemove",
"pyrogram.ForceReply"] = None,
progress: callable = None,
progress_args: tuple = ()) -> Union["pyrogram.Message", None]:
def send_audio(
self,
chat_id: Union[int, str],
audio: str,
caption: str = "",
parse_mode: str = "",
duration: int = 0,
performer: str = None,
title: str = None,
thumb: str = None,
disable_notification: bool = None,
reply_to_message_id: int = None,
reply_markup: Union[
"pyrogram.InlineKeyboardMarkup",
"pyrogram.ReplyKeyboardMarkup",
"pyrogram.ReplyKeyboardRemove",
"pyrogram.ForceReply"
] = None,
progress: callable = None,
progress_args: tuple = ()
) -> Union["pyrogram.Message", None]:
"""Use this method to send audio files.
For sending voice messages, use the :obj:`send_voice()` method instead.
@@ -123,7 +127,7 @@ class SendAudio(BaseClient):
In case the upload is deliberately stopped with :meth:`stop_transmission`, None is returned instead.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
"""
file = None
style = self.html if parse_mode.lower() == "html" else self.markdown
@@ -142,7 +146,7 @@ class SendAudio(BaseClient):
performer=performer,
title=title
),
types.DocumentAttributeFilename(os.path.basename(audio))
types.DocumentAttributeFilename(file_name=os.path.basename(audio))
]
)
elif audio.startswith("http"):

View File

@@ -0,0 +1,135 @@
# 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/>.
import binascii
import struct
from typing import Union
import pyrogram
from pyrogram.api import functions, types
from pyrogram.errors import FileIdInvalid
from pyrogram.client.ext import BaseClient, utils
class SendCachedMedia(BaseClient):
def send_cached_media(
self,
chat_id: Union[int, str],
file_id: str,
caption: str = "",
parse_mode: str = "",
disable_notification: bool = None,
reply_to_message_id: int = None,
reply_markup: Union[
"pyrogram.InlineKeyboardMarkup",
"pyrogram.ReplyKeyboardMarkup",
"pyrogram.ReplyKeyboardRemove",
"pyrogram.ForceReply"
] = None
) -> Union["pyrogram.Message", None]:
"""Use this method to send any media stored on the Telegram servers using a file_id.
This convenience method works with any valid file_id only.
It does the same as calling the relevant method for sending media using a file_id, thus saving you from the
hassle of using the correct method for the media the file_id is pointing to.
Args:
chat_id (``int`` | ``str``):
Unique identifier (int) or username (str) of the target chat.
For your personal cloud (Saved Messages) you can simply use "me" or "self".
For a contact that exists in your Telegram address book you can use his phone number (str).
file_id (``str``):
Media to send.
Pass a file_id as string to send a media that exists on the Telegram servers.
caption (``bool``, *optional*):
Media caption, 0-1024 characters.
parse_mode (``str``, *optional*):
Use :obj:`MARKDOWN <pyrogram.ParseMode.MARKDOWN>` or :obj:`HTML <pyrogram.ParseMode.HTML>`
if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in your caption.
Defaults to Markdown.
disable_notification (``bool``, *optional*):
Sends the message silently.
Users will receive a notification with no sound.
reply_to_message_id (``int``, *optional*):
If the message is a reply, ID of the original message.
reply_markup (:obj:`InlineKeyboardMarkup` | :obj:`ReplyKeyboardMarkup` | :obj:`ReplyKeyboardRemove` | :obj:`ForceReply`, *optional*):
Additional interface options. An object for an inline keyboard, custom reply keyboard,
instructions to remove reply keyboard or to force a reply from the user.
Returns:
On success, the sent :obj:`Message <pyrogram.Message>` is returned.
Raises:
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
"""
style = self.html if parse_mode.lower() == "html" else self.markdown
try:
decoded = utils.decode(file_id)
fmt = "<iiqqqqi" if len(decoded) > 24 else "<iiqq"
unpacked = struct.unpack(fmt, decoded)
except (AssertionError, binascii.Error, struct.error):
raise FileIdInvalid from None
else:
media_type = BaseClient.MEDIA_TYPE_ID.get(unpacked[0], None)
if not media_type:
raise FileIdInvalid("Unknown media type: {}".format(unpacked[0]))
if media_type == "photo":
media = types.InputMediaPhoto(
id=types.InputPhoto(
id=unpacked[2],
access_hash=unpacked[3],
file_reference=b""
)
)
else:
media = types.InputMediaDocument(
id=types.InputDocument(
id=unpacked[2],
access_hash=unpacked[3],
file_reference=b""
)
)
r = self.send(
functions.messages.SendMedia(
peer=self.resolve_peer(chat_id),
media=media,
silent=disable_notification or None,
reply_to_msg_id=reply_to_message_id,
random_id=self.rnd_id(),
reply_markup=reply_markup.write() if reply_markup else None,
**style.parse(caption)
)
)
for i in r.updates:
if isinstance(i, (types.UpdateNewMessage, types.UpdateNewChannelMessage)):
return pyrogram.Message._parse(
self, i.message,
{i.id: i for i in r.users},
{i.id: i for i in r.chats}
)

View File

@@ -23,10 +23,12 @@ from pyrogram.client.ext import BaseClient, ChatAction
class SendChatAction(BaseClient):
def send_chat_action(self,
chat_id: Union[int, str],
action: Union[ChatAction, str],
progress: int = 0):
def send_chat_action(
self,
chat_id: Union[int, str],
action: Union[ChatAction, str],
progress: int = 0
):
"""Use this method when you need to tell the other party that something is happening on your side.
Args:
@@ -49,7 +51,7 @@ class SendChatAction(BaseClient):
On success, True is returned.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
``ValueError`` if the provided string is not a valid ChatAction.
"""
@@ -60,7 +62,7 @@ class SendChatAction(BaseClient):
action = action.value
if "Upload" in action.__name__:
action = action(progress)
action = action(progress=progress)
else:
action = action()

View File

@@ -24,18 +24,22 @@ from pyrogram.client.ext import BaseClient
class SendContact(BaseClient):
def send_contact(self,
chat_id: Union[int, str],
phone_number: str,
first_name: str,
last_name: str = "",
vcard: str = "",
disable_notification: bool = None,
reply_to_message_id: int = None,
reply_markup: Union["pyrogram.InlineKeyboardMarkup",
"pyrogram.ReplyKeyboardMarkup",
"pyrogram.ReplyKeyboardRemove",
"pyrogram.ForceReply"] = None) -> "pyrogram.Message":
def send_contact(
self,
chat_id: Union[int, str],
phone_number: str,
first_name: str,
last_name: str = None,
vcard: str = None,
disable_notification: bool = None,
reply_to_message_id: int = None,
reply_markup: Union[
"pyrogram.InlineKeyboardMarkup",
"pyrogram.ReplyKeyboardMarkup",
"pyrogram.ReplyKeyboardRemove",
"pyrogram.ForceReply"
] = None
) -> "pyrogram.Message":
"""Use this method to send phone contacts.
Args:
@@ -71,7 +75,7 @@ class SendContact(BaseClient):
On success, the sent :obj:`Message <pyrogram.Message>` is returned.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
"""
r = self.send(
functions.messages.SendMedia(
@@ -79,8 +83,8 @@ class SendContact(BaseClient):
media=types.InputMediaContact(
phone_number=phone_number,
first_name=first_name,
last_name=last_name,
vcard=vcard
last_name=last_name or "",
vcard=vcard or ""
),
message="",
silent=disable_notification or None,

View File

@@ -23,25 +23,29 @@ from typing import Union
import pyrogram
from pyrogram.api import functions, types
from pyrogram.api.errors import FileIdInvalid, FilePartMissing
from pyrogram.errors import FileIdInvalid, FilePartMissing
from pyrogram.client.ext import BaseClient, utils
class SendDocument(BaseClient):
def send_document(self,
chat_id: Union[int, str],
document: str,
thumb: str = None,
caption: str = "",
parse_mode: str = "",
disable_notification: bool = None,
reply_to_message_id: int = None,
reply_markup: Union["pyrogram.InlineKeyboardMarkup",
"pyrogram.ReplyKeyboardMarkup",
"pyrogram.ReplyKeyboardRemove",
"pyrogram.ForceReply"] = None,
progress: callable = None,
progress_args: tuple = ()) -> Union["pyrogram.Message", None]:
def send_document(
self,
chat_id: Union[int, str],
document: str,
thumb: str = None,
caption: str = "",
parse_mode: str = "",
disable_notification: bool = None,
reply_to_message_id: int = None,
reply_markup: Union[
"pyrogram.InlineKeyboardMarkup",
"pyrogram.ReplyKeyboardMarkup",
"pyrogram.ReplyKeyboardRemove",
"pyrogram.ForceReply"
] = None,
progress: callable = None,
progress_args: tuple = ()
) -> Union["pyrogram.Message", None]:
"""Use this method to send general files.
Args:
@@ -56,7 +60,7 @@ class SendDocument(BaseClient):
pass an HTTP URL as a string for Telegram to get a file from the Internet, or
pass a file path as string to upload a new file that exists on your local machine.
thumb (``str``):
thumb (``str``, *optional*):
Thumbnail of the file sent.
The thumbnail should be in JPEG format and less than 200 KB in size.
A thumbnail's width and height should not exceed 90 pixels.
@@ -109,7 +113,7 @@ class SendDocument(BaseClient):
In case the upload is deliberately stopped with :meth:`stop_transmission`, None is returned instead.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
"""
file = None
style = self.html if parse_mode.lower() == "html" else self.markdown
@@ -123,7 +127,7 @@ class SendDocument(BaseClient):
file=file,
thumb=thumb,
attributes=[
types.DocumentAttributeFilename(os.path.basename(document))
types.DocumentAttributeFilename(file_name=os.path.basename(document))
]
)
elif document.startswith("http"):

View File

@@ -24,16 +24,20 @@ from pyrogram.client.ext import BaseClient
class SendLocation(BaseClient):
def send_location(self,
chat_id: Union[int, str],
latitude: float,
longitude: float,
disable_notification: bool = None,
reply_to_message_id: int = None,
reply_markup: Union["pyrogram.InlineKeyboardMarkup",
"pyrogram.ReplyKeyboardMarkup",
"pyrogram.ReplyKeyboardRemove",
"pyrogram.ForceReply"] = None) -> "pyrogram.Message":
def send_location(
self,
chat_id: Union[int, str],
latitude: float,
longitude: float,
disable_notification: bool = None,
reply_to_message_id: int = None,
reply_markup: Union[
"pyrogram.InlineKeyboardMarkup",
"pyrogram.ReplyKeyboardMarkup",
"pyrogram.ReplyKeyboardRemove",
"pyrogram.ForceReply"
] = None
) -> "pyrogram.Message":
"""Use this method to send points on the map.
Args:
@@ -63,15 +67,15 @@ class SendLocation(BaseClient):
On success, the sent :obj:`Message <pyrogram.Message>` is returned.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
"""
r = self.send(
functions.messages.SendMedia(
peer=self.resolve_peer(chat_id),
media=types.InputMediaGeoPoint(
types.InputGeoPoint(
latitude,
longitude
geo_point=types.InputGeoPoint(
lat=latitude,
long=longitude
)
),
message="",

View File

@@ -25,7 +25,7 @@ from typing import Union, List
import pyrogram
from pyrogram.api import functions, types
from pyrogram.api.errors import FileIdInvalid, FloodWait
from pyrogram.errors import FileIdInvalid, FloodWait
from pyrogram.client.ext import BaseClient, utils
log = logging.getLogger(__name__)
@@ -34,11 +34,13 @@ log = logging.getLogger(__name__)
class SendMediaGroup(BaseClient):
# TODO: Add progress parameter
# TODO: Figure out how to send albums using URLs
def send_media_group(self,
chat_id: Union[int, str],
media: List[Union["pyrogram.InputMediaPhoto", "pyrogram.InputMediaVideo"]],
disable_notification: bool = None,
reply_to_message_id: int = None):
def send_media_group(
self,
chat_id: Union[int, str],
media: List[Union["pyrogram.InputMediaPhoto", "pyrogram.InputMediaVideo"]],
disable_notification: bool = None,
reply_to_message_id: int = None
):
"""Use this method to send a group of photos or videos as an album.
Args:
@@ -47,10 +49,8 @@ class SendMediaGroup(BaseClient):
For your personal cloud (Saved Messages) you can simply use "me" or "self".
For a contact that exists in your Telegram address book you can use his phone number (str).
media (``list``):
A list containing either :obj:`InputMediaPhoto <pyrogram.InputMediaPhoto>` or
:obj:`InputMediaVideo <pyrogram.InputMediaVideo>` objects
describing photos and videos to be sent, must include 210 items.
media (List of :obj:`InputMediaPhoto` and :obj:`InputMediaVideo`):
A list describing photos and videos to be sent, must include 210 items.
disable_notification (``bool``, *optional*):
Sends the message silently.
@@ -64,7 +64,7 @@ class SendMediaGroup(BaseClient):
single messages sent.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
"""
multi_media = []
@@ -137,7 +137,7 @@ class SendMediaGroup(BaseClient):
w=i.width,
h=i.height
),
types.DocumentAttributeFilename(os.path.basename(i.media))
types.DocumentAttributeFilename(file_name=os.path.basename(i.media))
]
)
)

View File

@@ -24,17 +24,21 @@ from ...ext import BaseClient
class SendMessage(BaseClient):
def send_message(self,
chat_id: Union[int, str],
text: str,
parse_mode: str = "",
disable_web_page_preview: bool = None,
disable_notification: bool = None,
reply_to_message_id: int = None,
reply_markup: Union["pyrogram.InlineKeyboardMarkup",
"pyrogram.ReplyKeyboardMarkup",
"pyrogram.ReplyKeyboardRemove",
"pyrogram.ForceReply"] = None) -> "pyrogram.Message":
def send_message(
self,
chat_id: Union[int, str],
text: str,
parse_mode: str = "",
disable_web_page_preview: bool = None,
disable_notification: bool = None,
reply_to_message_id: int = None,
reply_markup: Union[
"pyrogram.InlineKeyboardMarkup",
"pyrogram.ReplyKeyboardMarkup",
"pyrogram.ReplyKeyboardRemove",
"pyrogram.ForceReply"
] = None
) -> "pyrogram.Message":
"""Use this method to send text messages.
Args:
@@ -69,7 +73,7 @@ class SendMessage(BaseClient):
On success, the sent :obj:`Message` is returned.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
"""
style = self.html if parse_mode.lower() == "html" else self.markdown
message, entities = style.parse(text).values()

View File

@@ -23,25 +23,29 @@ from typing import Union
import pyrogram
from pyrogram.api import functions, types
from pyrogram.api.errors import FileIdInvalid, FilePartMissing
from pyrogram.errors import FileIdInvalid, FilePartMissing
from pyrogram.client.ext import BaseClient, utils
class SendPhoto(BaseClient):
def send_photo(self,
chat_id: Union[int, str],
photo: str,
caption: str = "",
parse_mode: str = "",
ttl_seconds: int = None,
disable_notification: bool = None,
reply_to_message_id: int = None,
reply_markup: Union["pyrogram.InlineKeyboardMarkup",
"pyrogram.ReplyKeyboardMarkup",
"pyrogram.ReplyKeyboardRemove",
"pyrogram.ForceReply"] = None,
progress: callable = None,
progress_args: tuple = ()) -> Union["pyrogram.Message", None]:
def send_photo(
self,
chat_id: Union[int, str],
photo: str,
caption: str = "",
parse_mode: str = "",
ttl_seconds: int = None,
disable_notification: bool = None,
reply_to_message_id: int = None,
reply_markup: Union[
"pyrogram.InlineKeyboardMarkup",
"pyrogram.ReplyKeyboardMarkup",
"pyrogram.ReplyKeyboardRemove",
"pyrogram.ForceReply"
] = None,
progress: callable = None,
progress_args: tuple = ()
) -> Union["pyrogram.Message", None]:
"""Use this method to send photos.
Args:
@@ -108,7 +112,7 @@ class SendPhoto(BaseClient):
In case the upload is deliberately stopped with :meth:`stop_transmission`, None is returned instead.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
"""
file = None
style = self.html if parse_mode.lower() == "html" else self.markdown

View File

@@ -24,16 +24,20 @@ from pyrogram.client.ext import BaseClient
class SendPoll(BaseClient):
def send_poll(self,
chat_id: Union[int, str],
question: str,
options: List[str],
disable_notification: bool = None,
reply_to_message_id: int = None,
reply_markup: Union["pyrogram.InlineKeyboardMarkup",
"pyrogram.ReplyKeyboardMarkup",
"pyrogram.ReplyKeyboardRemove",
"pyrogram.ForceReply"] = None) -> "pyrogram.Message":
def send_poll(
self,
chat_id: Union[int, str],
question: str,
options: List[str],
disable_notification: bool = None,
reply_to_message_id: int = None,
reply_markup: Union[
"pyrogram.InlineKeyboardMarkup",
"pyrogram.ReplyKeyboardMarkup",
"pyrogram.ReplyKeyboardRemove",
"pyrogram.ForceReply"
] = None
) -> "pyrogram.Message":
"""Use this method to send a new poll.
Args:
@@ -63,7 +67,7 @@ class SendPoll(BaseClient):
On success, the sent :obj:`Message <pyrogram.Message>` is returned.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
"""
r = self.send(
functions.messages.SendMedia(

View File

@@ -23,22 +23,26 @@ from typing import Union
import pyrogram
from pyrogram.api import functions, types
from pyrogram.api.errors import FileIdInvalid, FilePartMissing
from pyrogram.errors import FileIdInvalid, FilePartMissing
from pyrogram.client.ext import BaseClient, utils
class SendSticker(BaseClient):
def send_sticker(self,
chat_id: Union[int, str],
sticker: str,
disable_notification: bool = None,
reply_to_message_id: int = None,
reply_markup: Union["pyrogram.InlineKeyboardMarkup",
"pyrogram.ReplyKeyboardMarkup",
"pyrogram.ReplyKeyboardRemove",
"pyrogram.ForceReply"] = None,
progress: callable = None,
progress_args: tuple = ()) -> Union["pyrogram.Message", None]:
def send_sticker(
self,
chat_id: Union[int, str],
sticker: str,
disable_notification: bool = None,
reply_to_message_id: int = None,
reply_markup: Union[
"pyrogram.InlineKeyboardMarkup",
"pyrogram.ReplyKeyboardMarkup",
"pyrogram.ReplyKeyboardRemove",
"pyrogram.ForceReply"
] = None,
progress: callable = None,
progress_args: tuple = ()
) -> Union["pyrogram.Message", None]:
"""Use this method to send .webp stickers.
Args:
@@ -92,7 +96,7 @@ class SendSticker(BaseClient):
In case the upload is deliberately stopped with :meth:`stop_transmission`, None is returned instead.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
"""
file = None
@@ -103,7 +107,7 @@ class SendSticker(BaseClient):
mime_type="image/webp",
file=file,
attributes=[
types.DocumentAttributeFilename(os.path.basename(sticker))
types.DocumentAttributeFilename(file_name=os.path.basename(sticker))
]
)
elif sticker.startswith("http"):

View File

@@ -24,20 +24,24 @@ from pyrogram.client.ext import BaseClient
class SendVenue(BaseClient):
def send_venue(self,
chat_id: Union[int, str],
latitude: float,
longitude: float,
title: str,
address: str,
foursquare_id: str = "",
foursquare_type: str = "",
disable_notification: bool = None,
reply_to_message_id: int = None,
reply_markup: Union["pyrogram.InlineKeyboardMarkup",
"pyrogram.ReplyKeyboardMarkup",
"pyrogram.ReplyKeyboardRemove",
"pyrogram.ForceReply"] = None) -> "pyrogram.Message":
def send_venue(
self,
chat_id: Union[int, str],
latitude: float,
longitude: float,
title: str,
address: str,
foursquare_id: str = "",
foursquare_type: str = "",
disable_notification: bool = None,
reply_to_message_id: int = None,
reply_markup: Union[
"pyrogram.InlineKeyboardMarkup",
"pyrogram.ReplyKeyboardMarkup",
"pyrogram.ReplyKeyboardRemove",
"pyrogram.ForceReply"
] = None
) -> "pyrogram.Message":
"""Use this method to send information about a venue.
Args:
@@ -80,7 +84,7 @@ class SendVenue(BaseClient):
On success, the sent :obj:`Message <pyrogram.Message>` is returned.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
"""
r = self.send(
functions.messages.SendMedia(

View File

@@ -23,29 +23,33 @@ from typing import Union
import pyrogram
from pyrogram.api import functions, types
from pyrogram.api.errors import FileIdInvalid, FilePartMissing
from pyrogram.errors import FileIdInvalid, FilePartMissing
from pyrogram.client.ext import BaseClient, utils
class SendVideo(BaseClient):
def send_video(self,
chat_id: Union[int, str],
video: str,
caption: str = "",
parse_mode: str = "",
duration: int = 0,
width: int = 0,
height: int = 0,
thumb: str = None,
supports_streaming: bool = True,
disable_notification: bool = None,
reply_to_message_id: int = None,
reply_markup: Union["pyrogram.InlineKeyboardMarkup",
"pyrogram.ReplyKeyboardMarkup",
"pyrogram.ReplyKeyboardRemove",
"pyrogram.ForceReply"] = None,
progress: callable = None,
progress_args: tuple = ()) -> Union["pyrogram.Message", None]:
def send_video(
self,
chat_id: Union[int, str],
video: str,
caption: str = "",
parse_mode: str = "",
duration: int = 0,
width: int = 0,
height: int = 0,
thumb: str = None,
supports_streaming: bool = True,
disable_notification: bool = None,
reply_to_message_id: int = None,
reply_markup: Union[
"pyrogram.InlineKeyboardMarkup",
"pyrogram.ReplyKeyboardMarkup",
"pyrogram.ReplyKeyboardRemove",
"pyrogram.ForceReply"
] = None,
progress: callable = None,
progress_args: tuple = ()
) -> Union["pyrogram.Message", None]:
"""Use this method to send video files.
Args:
@@ -125,7 +129,7 @@ class SendVideo(BaseClient):
In case the upload is deliberately stopped with :meth:`stop_transmission`, None is returned instead.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
"""
file = None
style = self.html if parse_mode.lower() == "html" else self.markdown
@@ -145,7 +149,7 @@ class SendVideo(BaseClient):
w=width,
h=height
),
types.DocumentAttributeFilename(os.path.basename(video))
types.DocumentAttributeFilename(file_name=os.path.basename(video))
]
)
elif video.startswith("http"):

View File

@@ -23,25 +23,29 @@ from typing import Union
import pyrogram
from pyrogram.api import functions, types
from pyrogram.api.errors import FileIdInvalid, FilePartMissing
from pyrogram.errors import FileIdInvalid, FilePartMissing
from pyrogram.client.ext import BaseClient, utils
class SendVideoNote(BaseClient):
def send_video_note(self,
chat_id: Union[int, str],
video_note: str,
duration: int = 0,
length: int = 1,
thumb: str = None,
disable_notification: bool = None,
reply_to_message_id: int = None,
reply_markup: Union["pyrogram.InlineKeyboardMarkup",
"pyrogram.ReplyKeyboardMarkup",
"pyrogram.ReplyKeyboardRemove",
"pyrogram.ForceReply"] = None,
progress: callable = None,
progress_args: tuple = ()) -> Union["pyrogram.Message", None]:
def send_video_note(
self,
chat_id: Union[int, str],
video_note: str,
duration: int = 0,
length: int = 1,
thumb: str = None,
disable_notification: bool = None,
reply_to_message_id: int = None,
reply_markup: Union[
"pyrogram.InlineKeyboardMarkup",
"pyrogram.ReplyKeyboardMarkup",
"pyrogram.ReplyKeyboardRemove",
"pyrogram.ForceReply"
] = None,
progress: callable = None,
progress_args: tuple = ()
) -> Union["pyrogram.Message", None]:
"""Use this method to send video messages.
Args:
@@ -107,7 +111,7 @@ class SendVideoNote(BaseClient):
In case the upload is deliberately stopped with :meth:`stop_transmission`, None is returned instead.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
"""
file = None

View File

@@ -23,25 +23,29 @@ from typing import Union
import pyrogram
from pyrogram.api import functions, types
from pyrogram.api.errors import FileIdInvalid, FilePartMissing
from pyrogram.errors import FileIdInvalid, FilePartMissing
from pyrogram.client.ext import BaseClient, utils
class SendVoice(BaseClient):
def send_voice(self,
chat_id: Union[int, str],
voice: str,
caption: str = "",
parse_mode: str = "",
duration: int = 0,
disable_notification: bool = None,
reply_to_message_id: int = None,
reply_markup: Union["pyrogram.InlineKeyboardMarkup",
"pyrogram.ReplyKeyboardMarkup",
"pyrogram.ReplyKeyboardRemove",
"pyrogram.ForceReply"] = None,
progress: callable = None,
progress_args: tuple = ()) -> Union["pyrogram.Message", None]:
def send_voice(
self,
chat_id: Union[int, str],
voice: str,
caption: str = "",
parse_mode: str = "",
duration: int = 0,
disable_notification: bool = None,
reply_to_message_id: int = None,
reply_markup: Union[
"pyrogram.InlineKeyboardMarkup",
"pyrogram.ReplyKeyboardMarkup",
"pyrogram.ReplyKeyboardRemove",
"pyrogram.ForceReply"
] = None,
progress: callable = None,
progress_args: tuple = ()
) -> Union["pyrogram.Message", None]:
"""Use this method to send audio files.
Args:
@@ -106,7 +110,7 @@ class SendVoice(BaseClient):
In case the upload is deliberately stopped with :meth:`stop_transmission`, None is returned instead.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
"""
file = None
style = self.html if parse_mode.lower() == "html" else self.markdown

View File

@@ -23,10 +23,12 @@ from pyrogram.client.ext import BaseClient
class VotePoll(BaseClient):
def vote_poll(self,
chat_id: Union[int, str],
message_id: id,
option: int) -> bool:
def vote_poll(
self,
chat_id: Union[int, str],
message_id: id,
option: int
) -> bool:
"""Use this method to vote a poll.
Args:
@@ -45,7 +47,7 @@ class VotePoll(BaseClient):
On success, True is returned.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
"""
poll = self.get_messages(chat_id, message_id).poll

View File

@@ -24,10 +24,12 @@ from ...ext import BaseClient
class ChangeCloudPassword(BaseClient):
def change_cloud_password(self,
current_password: str,
new_password: str,
new_hint: str = "") -> bool:
def change_cloud_password(
self,
current_password: str,
new_password: str,
new_hint: str = ""
) -> bool:
"""Use this method to change your Two-Step Verification password (Cloud Password) with a new one.
Args:
@@ -44,7 +46,7 @@ class ChangeCloudPassword(BaseClient):
True on success.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
``ValueError`` in case there is no cloud password to change.
"""
r = self.send(functions.account.GetPassword())

View File

@@ -24,10 +24,12 @@ from ...ext import BaseClient
class EnableCloudPassword(BaseClient):
def enable_cloud_password(self,
password: str,
hint: str = "",
email: str = None) -> bool:
def enable_cloud_password(
self,
password: str,
hint: str = "",
email: str = None
) -> bool:
"""Use this method to enable the Two-Step Verification security feature (Cloud Password) on your account.
This password will be asked when you log-in on a new device in addition to the SMS code.
@@ -46,7 +48,7 @@ class EnableCloudPassword(BaseClient):
True on success.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
``ValueError`` in case there is already a cloud password enabled.
"""
r = self.send(functions.account.GetPassword())

View File

@@ -22,8 +22,10 @@ from ...ext import BaseClient
class RemoveCloudPassword(BaseClient):
def remove_cloud_password(self,
password: str) -> bool:
def remove_cloud_password(
self,
password: str
) -> bool:
"""Use this method to turn off the Two-Step Verification security feature (Cloud Password) on your account.
Args:
@@ -34,7 +36,7 @@ class RemoveCloudPassword(BaseClient):
True on success.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
``ValueError`` in case there is no cloud password to remove.
"""
r = self.send(functions.account.GetPassword())

View File

@@ -101,4 +101,4 @@ def compute_check(r: types.account.Password, password: str) -> types.InputCheckP
+ K_bytes
)
return types.InputCheckPasswordSRP(srp_id, A_bytes, M1_bytes)
return types.InputCheckPasswordSRP(srp_id=srp_id, A=A_bytes, M1=M1_bytes)

View File

@@ -21,6 +21,7 @@ from .get_me import GetMe
from .get_user_profile_photos import GetUserProfilePhotos
from .get_users import GetUsers
from .set_user_profile_photo import SetUserProfilePhoto
from .update_username import UpdateUsername
class Users(
@@ -28,6 +29,7 @@ class Users(
SetUserProfilePhoto,
DeleteUserProfilePhotos,
GetUsers,
GetMe
GetMe,
UpdateUsername
):
pass

View File

@@ -25,9 +25,11 @@ from ...ext import BaseClient
class DeleteUserProfilePhotos(BaseClient):
def delete_user_profile_photos(self,
id: Union[str, List[str]]) -> bool:
"""Use this method to delete your own profile photos
def delete_user_profile_photos(
self,
id: Union[str, List[str]]
) -> bool:
"""Use this method to delete your own profile photos.
Args:
id (``str`` | ``list``):
@@ -38,7 +40,7 @@ class DeleteUserProfilePhotos(BaseClient):
True on success.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
"""
id = id if isinstance(id, list) else [id]
input_photos = []

View File

@@ -29,13 +29,13 @@ class GetMe(BaseClient):
Basic information about the user or bot in form of a :obj:`User` object
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
"""
return pyrogram.User._parse(
self,
self.send(
functions.users.GetFullUser(
types.InputPeerSelf()
id=types.InputPeerSelf()
)
).user
)

View File

@@ -24,10 +24,12 @@ from ...ext import BaseClient
class GetUserProfilePhotos(BaseClient):
def get_user_profile_photos(self,
user_id: Union[int, str],
offset: int = 0,
limit: int = 100) -> "pyrogram.UserProfilePhotos":
def get_user_profile_photos(
self,
user_id: Union[int, str],
offset: int = 0,
limit: int = 100
) -> "pyrogram.UserProfilePhotos":
"""Use this method to get a list of profile pictures for a user.
Args:
@@ -48,7 +50,7 @@ class GetUserProfilePhotos(BaseClient):
On success, a :obj:`UserProfilePhotos` object is returned.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
"""
return pyrogram.UserProfilePhotos._parse(
self,

View File

@@ -24,8 +24,10 @@ from ...ext import BaseClient
class GetUsers(BaseClient):
def get_users(self,
user_ids: Iterable[Union[int, str]]) -> Union["pyrogram.User", List["pyrogram.User"]]:
def get_users(
self,
user_ids: Iterable[Union[int, str]]
) -> Union["pyrogram.User", List["pyrogram.User"]]:
"""Use this method to get information about a user.
You can retrieve up to 200 users at once.
@@ -41,7 +43,7 @@ class GetUsers(BaseClient):
*user_ids* was an integer or string, the single requested :obj:`User` is returned.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
"""
is_iterable = not isinstance(user_ids, (int, str))
user_ids = list(user_ids) if is_iterable else [user_ids]

View File

@@ -21,8 +21,10 @@ from ...ext import BaseClient
class SetUserProfilePhoto(BaseClient):
def set_user_profile_photo(self,
photo: str) -> bool:
def set_user_profile_photo(
self,
photo: str
) -> bool:
"""Use this method to set a new profile photo.
This method only works for Users.
@@ -37,13 +39,13 @@ class SetUserProfilePhoto(BaseClient):
True on success.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
"""
return bool(
self.send(
functions.photos.UploadProfilePhoto(
self.save_file(photo)
file=self.save_file(photo)
)
)
)

Some files were not shown because too many files have changed in this diff Show More