Merge branch 'develop' into asyncio

# Conflicts:
#	pyrogram/client/client.py
#	pyrogram/client/methods/chats/join_chat.py
#	pyrogram/client/methods/messages/edit_message_media.py
This commit is contained in:
Dan 2019-03-02 16:36:44 +01:00
commit 02a1dde399
22 changed files with 187 additions and 35 deletions

View File

@ -20,7 +20,9 @@ Pyrogram
**Pyrogram** is an elegant, easy-to-use Telegram_ client library and framework written from the ground up in Python and C. **Pyrogram** is an elegant, easy-to-use Telegram_ client library and framework written from the ground up in Python and C.
It enables you to easily create custom apps using both user and bot identities (bot API alternative) via the `MTProto API`_. It enables you to easily create custom apps using both user and bot identities (bot API alternative) via the `MTProto API`_.
`A fully-asynchronous variant is also available » <https://github.com/pyrogram/pyrogram/issues/181>`_ `Pyrogram in fully-asynchronous mode is also available » <https://github.com/pyrogram/pyrogram/issues/181>`_
`Working PoC of Telegram voice calls using Pyrogram » <https://github.com/bakatrouble/pytgvoip>`_
Features Features
-------- --------

View File

@ -88,5 +88,6 @@ MEDIA_INVALID The media is invalid
BOT_SCORE_NOT_MODIFIED The bot score was not modified BOT_SCORE_NOT_MODIFIED The bot score was not modified
USER_BOT_REQUIRED The method can be used by bots only USER_BOT_REQUIRED The method can be used by bots only
IMAGE_PROCESS_FAILED The server failed to process your image IMAGE_PROCESS_FAILED The server failed to process your image
USERNAME_NOT_MODIFIED The username was not modified
CALL_ALREADY_ACCEPTED The call is already accepted CALL_ALREADY_ACCEPTED The call is already accepted
CALL_ALREADY_DECLINED The call is already declined CALL_ALREADY_DECLINED The call is already declined

1 id message
88 BOT_SCORE_NOT_MODIFIED The bot score was not modified
89 USER_BOT_REQUIRED The method can be used by bots only
90 IMAGE_PROCESS_FAILED The server failed to process your image
91 USERNAME_NOT_MODIFIED The username was not modified
92 CALL_ALREADY_ACCEPTED The call is already accepted
93 CALL_ALREADY_DECLINED The call is already declined

View File

@ -13,4 +13,4 @@ with app:
app.send_location("me", 51.500729, -0.124583) app.send_location("me", 51.500729, -0.124583)
# Send a sticker # Send a sticker
app.send_sticker("me", "CAADBAADhw4AAvLQYAHICbZ5SUs_jwI") app.send_sticker("me", "CAADBAADyg4AAvLQYAEYD4F7vcZ43AI")

View File

@ -30,6 +30,7 @@ import shutil
import struct import struct
import tempfile import tempfile
import time import time
import warnings
from configparser import ConfigParser from configparser import ConfigParser
from datetime import datetime from datetime import datetime
from hashlib import sha256, md5 from hashlib import sha256, md5
@ -68,10 +69,10 @@ class Client(Methods, BaseClient):
Args: Args:
session_name (``str``): session_name (``str``):
Name to uniquely identify a session of either a User or a Bot. Name to uniquely identify a session of either a User or a Bot, e.g.: "my_account". This name will be used
For Users: pass a string of your choice, e.g.: "my_main_account". to save a file to disk that stores details needed for reconnecting without asking again for credentials.
For Bots: pass your Bot API token, e.g.: "123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11" Note for bots: You can pass a bot token here, but this usage will be deprecated in next releases.
Note: as long as a valid User session file exists, Pyrogram won't ask you again to input your phone number. Use *bot_token* instead.
api_id (``int``, *optional*): api_id (``int``, *optional*):
The *api_id* part of your Telegram API Key, as integer. E.g.: 12345 The *api_id* part of your Telegram API Key, as integer. E.g.: 12345
@ -145,6 +146,10 @@ class Client(Methods, BaseClient):
a new Telegram account in case the phone number you passed is not registered yet. a new Telegram account in case the phone number you passed is not registered yet.
Only applicable for new sessions. Only applicable for new sessions.
bot_token (``str``, *optional*):
Pass your Bot API token to create a bot session, e.g.: "123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11"
Only applicable for new sessions.
last_name (``str``, *optional*): last_name (``str``, *optional*):
Same purpose as *first_name*; pass a Last Name to avoid entering it manually. It can Same purpose as *first_name*; pass a Last Name to avoid entering it manually. It can
be an empty string: "". Only applicable for new sessions. be an empty string: "". Only applicable for new sessions.
@ -193,6 +198,7 @@ class Client(Methods, BaseClient):
password: str = None, password: str = None,
recovery_code: callable = None, recovery_code: callable = None,
force_sms: bool = False, force_sms: bool = False,
bot_token: str = None,
first_name: str = None, first_name: str = None,
last_name: str = None, last_name: str = None,
workers: int = BaseClient.WORKERS, workers: int = BaseClient.WORKERS,
@ -219,6 +225,7 @@ class Client(Methods, BaseClient):
self.password = password self.password = password
self.recovery_code = recovery_code self.recovery_code = recovery_code
self.force_sms = force_sms self.force_sms = force_sms
self.bot_token = bot_token
self.first_name = first_name self.first_name = first_name
self.last_name = last_name self.last_name = last_name
self.workers = workers self.workers = workers
@ -264,8 +271,13 @@ class Client(Methods, BaseClient):
raise ConnectionError("Client has already been started") raise ConnectionError("Client has already been started")
if self.BOT_TOKEN_RE.match(self.session_name): if self.BOT_TOKEN_RE.match(self.session_name):
self.is_bot = True
self.bot_token = self.session_name self.bot_token = self.session_name
self.session_name = self.session_name.split(":")[0] self.session_name = self.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)
self.load_config() self.load_config()
await self.load_session() await self.load_session()
@ -283,13 +295,15 @@ class Client(Methods, BaseClient):
try: try:
if self.user_id is None: if self.user_id is None:
if self.bot_token is None: if self.bot_token is None:
self.is_bot = False
await self.authorize_user() await self.authorize_user()
else: else:
self.is_bot = True
await self.authorize_bot() await self.authorize_bot()
self.save_session() self.save_session()
if self.bot_token is None: if not self.is_bot:
if self.takeout: if self.takeout:
self.takeout_id = (await self.send(functions.account.InitTakeoutSession())).id self.takeout_id = (await self.send(functions.account.InitTakeoutSession())).id
log.warning("Takeout session {} initiated".format(self.takeout_id)) log.warning("Takeout session {} initiated".format(self.takeout_id))
@ -1112,6 +1126,8 @@ class Client(Methods, BaseClient):
self.auth_key = base64.b64decode("".join(s["auth_key"])) self.auth_key = base64.b64decode("".join(s["auth_key"]))
self.user_id = s["user_id"] self.user_id = s["user_id"]
self.date = s.get("date", 0) self.date = s.get("date", 0)
# TODO: replace default with False once token session name will be deprecated
self.is_bot = s.get("is_bot", self.is_bot)
for k, v in s.get("peers_by_id", {}).items(): for k, v in s.get("peers_by_id", {}).items():
self.peers_by_id[int(k)] = utils.get_input_peer(int(k), v) self.peers_by_id[int(k)] = utils.get_input_peer(int(k), v)
@ -1138,7 +1154,7 @@ class Client(Methods, BaseClient):
if include is None: if include is None:
for path in sorted(Path(root).rglob("*.py")): for path in sorted(Path(root).rglob("*.py")):
module_path = os.path.splitext(str(path))[0].replace("/", ".") module_path = '.'.join(path.parent.parts + (path.stem,))
module = import_module(module_path) module = import_module(module_path)
for name in vars(module).keys(): for name in vars(module).keys():
@ -1245,7 +1261,8 @@ class Client(Methods, BaseClient):
test_mode=self.test_mode, test_mode=self.test_mode,
auth_key=auth_key, auth_key=auth_key,
user_id=self.user_id, user_id=self.user_id,
date=self.date date=self.date,
is_bot=self.is_bot,
), ),
f, f,
indent=4 indent=4

View File

@ -67,7 +67,7 @@ class BaseClient:
} }
def __init__(self): def __init__(self):
self.bot_token = None self.is_bot = None
self.dc_id = None self.dc_id = None
self.auth_key = None self.auth_key = None
self.user_id = None self.user_id = None

View File

@ -92,6 +92,7 @@ class Syncer:
auth_key=auth_key, auth_key=auth_key,
user_id=client.user_id, user_id=client.user_id,
date=int(time.time()), date=int(time.time()),
is_bot=client.is_bot,
peers_by_id={ peers_by_id={
k: getattr(v, "access_hash", None) k: getattr(v, "access_hash", None)
for k, v in client.peers_by_id.copy().items() for k, v in client.peers_by_id.copy().items()

View File

@ -37,6 +37,7 @@ from .set_chat_photo import SetChatPhoto
from .set_chat_title import SetChatTitle from .set_chat_title import SetChatTitle
from .unban_chat_member import UnbanChatMember from .unban_chat_member import UnbanChatMember
from .unpin_chat_message import UnpinChatMessage from .unpin_chat_message import UnpinChatMessage
from .update_chat_username import UpdateChatUsername
class Chats( class Chats(
@ -60,6 +61,7 @@ class Chats(
GetChatMembersCount, GetChatMembersCount,
GetChatPreview, GetChatPreview,
IterDialogs, IterDialogs,
IterChatMembers IterChatMembers,
UpdateChatUsername
): ):
pass pass

View File

@ -16,6 +16,7 @@
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>. # along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
import pyrogram
from pyrogram.api import functions, types from pyrogram.api import functions, types
from ...ext import BaseClient from ...ext import BaseClient
@ -30,17 +31,24 @@ class JoinChat(BaseClient):
Unique identifier for the target chat in form of a *t.me/joinchat/* link or username of the target 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). channel/supergroup (in the format @username).
Returns:
On success, a :obj:`Chat <pyrogram.Chat>` object is returned.
Raises: Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error. :class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
""" """
match = self.INVITE_LINK_RE.match(chat_id) match = self.INVITE_LINK_RE.match(chat_id)
if match: if match:
return await self.send( chat = await self.send(
functions.messages.ImportChatInvite( functions.messages.ImportChatInvite(
hash=match.group(1) 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: else:
resolved_peer = await self.send( resolved_peer = await self.send(
functions.contacts.ResolveUsername( functions.contacts.ResolveUsername(
@ -53,8 +61,10 @@ class JoinChat(BaseClient):
access_hash=resolved_peer.chats[0].access_hash access_hash=resolved_peer.chats[0].access_hash
) )
return await self.send( chat = await self.send(
functions.channels.JoinChannel( functions.channels.JoinChannel(
channel=channel channel=channel
) )
) )
return pyrogram.Chat._parse_channel_chat(self, chat.chats[0])

View File

@ -0,0 +1,59 @@
# 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:`Error <pyrogram.Error>` 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

@ -17,7 +17,6 @@
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>. # along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
import binascii import binascii
import mimetypes
import os import os
import struct import struct
from typing import Union from typing import Union
@ -122,7 +121,8 @@ class EditMessageMedia(BaseClient):
functions.messages.UploadMedia( functions.messages.UploadMedia(
peer=await self.resolve_peer(chat_id), peer=await self.resolve_peer(chat_id),
media=types.InputMediaUploadedDocument( media=types.InputMediaUploadedDocument(
mime_type=mimetypes.types_map[".mp4"], mime_type="video/mp4",
thumb=None if media.thumb is None else self.save_file(media.thumb),
file=await self.save_file(media.media), file=await self.save_file(media.media),
attributes=[ attributes=[
types.DocumentAttributeVideo( types.DocumentAttributeVideo(
@ -178,7 +178,8 @@ class EditMessageMedia(BaseClient):
functions.messages.UploadMedia( functions.messages.UploadMedia(
peer=await self.resolve_peer(chat_id), peer=await self.resolve_peer(chat_id),
media=types.InputMediaUploadedDocument( media=types.InputMediaUploadedDocument(
mime_type=mimetypes.types_map.get("." + media.media.split(".")[-1], "audio/mpeg"), mime_type="audio/mpeg",
thumb=None if media.thumb is None else self.save_file(media.thumb),
file=await self.save_file(media.media), file=await self.save_file(media.media),
attributes=[ attributes=[
types.DocumentAttributeAudio( types.DocumentAttributeAudio(
@ -233,7 +234,8 @@ class EditMessageMedia(BaseClient):
functions.messages.UploadMedia( functions.messages.UploadMedia(
peer=await self.resolve_peer(chat_id), peer=await self.resolve_peer(chat_id),
media=types.InputMediaUploadedDocument( media=types.InputMediaUploadedDocument(
mime_type=mimetypes.types_map[".mp4"], mime_type="video/mp4",
thumb=None if media.thumb is None else self.save_file(media.thumb),
file=await self.save_file(media.media), file=await self.save_file(media.media),
attributes=[ attributes=[
types.DocumentAttributeVideo( types.DocumentAttributeVideo(
@ -290,7 +292,8 @@ class EditMessageMedia(BaseClient):
functions.messages.UploadMedia( functions.messages.UploadMedia(
peer=await self.resolve_peer(chat_id), peer=await self.resolve_peer(chat_id),
media=types.InputMediaUploadedDocument( media=types.InputMediaUploadedDocument(
mime_type=mimetypes.types_map.get("." + media.media.split(".")[-1], "text/plain"), mime_type="application/zip",
thumb=None if media.thumb is None else self.save_file(media.thumb),
file=await self.save_file(media.media), file=await self.save_file(media.media),
attributes=[ attributes=[
types.DocumentAttributeFilename(os.path.basename(media.media)) types.DocumentAttributeFilename(os.path.basename(media.media))

View File

@ -17,7 +17,6 @@
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>. # along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
import binascii import binascii
import mimetypes
import os import os
import struct import struct
from typing import Union from typing import Union
@ -132,7 +131,7 @@ class SendAnimation(BaseClient):
thumb = None if thumb is None else await self.save_file(thumb) thumb = None if thumb is None else await self.save_file(thumb)
file = await self.save_file(animation, progress=progress, progress_args=progress_args) file = await self.save_file(animation, progress=progress, progress_args=progress_args)
media = types.InputMediaUploadedDocument( media = types.InputMediaUploadedDocument(
mime_type=mimetypes.types_map[".mp4"], mime_type="video/mp4",
file=file, file=file,
thumb=thumb, thumb=thumb,
attributes=[ attributes=[

View File

@ -17,7 +17,6 @@
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>. # along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
import binascii import binascii
import mimetypes
import os import os
import struct import struct
from typing import Union from typing import Union
@ -133,7 +132,7 @@ class SendAudio(BaseClient):
thumb = None if thumb is None else await self.save_file(thumb) thumb = None if thumb is None else await self.save_file(thumb)
file = await self.save_file(audio, progress=progress, progress_args=progress_args) file = await self.save_file(audio, progress=progress, progress_args=progress_args)
media = types.InputMediaUploadedDocument( media = types.InputMediaUploadedDocument(
mime_type=mimetypes.types_map.get("." + audio.split(".")[-1], "audio/mpeg"), mime_type="audio/mpeg",
file=file, file=file,
thumb=thumb, thumb=thumb,
attributes=[ attributes=[

View File

@ -17,7 +17,6 @@
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>. # along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
import binascii import binascii
import mimetypes
import os import os
import struct import struct
from typing import Union from typing import Union
@ -119,7 +118,7 @@ class SendDocument(BaseClient):
thumb = None if thumb is None else await self.save_file(thumb) thumb = None if thumb is None else await self.save_file(thumb)
file = await self.save_file(document, progress=progress, progress_args=progress_args) file = await self.save_file(document, progress=progress, progress_args=progress_args)
media = types.InputMediaUploadedDocument( media = types.InputMediaUploadedDocument(
mime_type=mimetypes.types_map.get("." + document.split(".")[-1], "text/plain"), mime_type="application/zip",
file=file, file=file,
thumb=thumb, thumb=thumb,
attributes=[ attributes=[

View File

@ -18,7 +18,6 @@
import binascii import binascii
import logging import logging
import mimetypes
import os import os
import struct import struct
from typing import Union, List from typing import Union, List
@ -130,7 +129,7 @@ class SendMediaGroup(BaseClient):
media=types.InputMediaUploadedDocument( media=types.InputMediaUploadedDocument(
file=await self.save_file(i.media), file=await self.save_file(i.media),
thumb=None if i.thumb is None else self.save_file(i.thumb), thumb=None if i.thumb is None else self.save_file(i.thumb),
mime_type=mimetypes.types_map[".mp4"], mime_type="video/mp4",
attributes=[ attributes=[
types.DocumentAttributeVideo( types.DocumentAttributeVideo(
supports_streaming=i.supports_streaming or None, supports_streaming=i.supports_streaming or None,

View File

@ -17,7 +17,6 @@
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>. # along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
import binascii import binascii
import mimetypes
import os import os
import struct import struct
from typing import Union from typing import Union
@ -136,7 +135,7 @@ class SendVideo(BaseClient):
thumb = None if thumb is None else await self.save_file(thumb) thumb = None if thumb is None else await self.save_file(thumb)
file = await self.save_file(video, progress=progress, progress_args=progress_args) file = await self.save_file(video, progress=progress, progress_args=progress_args)
media = types.InputMediaUploadedDocument( media = types.InputMediaUploadedDocument(
mime_type=mimetypes.types_map[".mp4"], mime_type="video/mp4",
file=file, file=file,
thumb=thumb, thumb=thumb,
attributes=[ attributes=[

View File

@ -17,7 +17,6 @@
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>. # along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
import binascii import binascii
import mimetypes
import os import os
import struct import struct
from typing import Union from typing import Union
@ -116,7 +115,7 @@ class SendVideoNote(BaseClient):
thumb = None if thumb is None else await self.save_file(thumb) thumb = None if thumb is None else await self.save_file(thumb)
file = await self.save_file(video_note, progress=progress, progress_args=progress_args) file = await self.save_file(video_note, progress=progress, progress_args=progress_args)
media = types.InputMediaUploadedDocument( media = types.InputMediaUploadedDocument(
mime_type=mimetypes.types_map[".mp4"], mime_type="video/mp4",
file=file, file=file,
thumb=thumb, thumb=thumb,
attributes=[ attributes=[

View File

@ -17,7 +17,6 @@
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>. # along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
import binascii import binascii
import mimetypes
import os import os
import struct import struct
from typing import Union from typing import Union
@ -116,7 +115,7 @@ class SendVoice(BaseClient):
if os.path.exists(voice): if os.path.exists(voice):
file = await self.save_file(voice, progress=progress, progress_args=progress_args) file = await self.save_file(voice, progress=progress, progress_args=progress_args)
media = types.InputMediaUploadedDocument( media = types.InputMediaUploadedDocument(
mime_type=mimetypes.types_map.get("." + voice.split(".")[-1], "audio/mpeg"), mime_type="audio/mpeg",
file=file, file=file,
attributes=[ attributes=[
types.DocumentAttributeAudio( types.DocumentAttributeAudio(

View File

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

View File

@ -0,0 +1,51 @@
# 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
from ...ext import BaseClient
class UpdateUsername(BaseClient):
def update_username(self,
username: Union[str, None]) -> bool:
"""Use this method to update your own username.
This method only works for users, not bots. Bot usernames must be changed via Bot Support or by recreating
them from scratch using BotFather. To update a channel or supergroup username you can use
:meth:`update_chat_username`.
Args:
username (``str`` | ``None``):
Username to set. "" (empty string) or None to remove the username.
Returns:
True on success.
Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
"""
return bool(
self.send(
functions.account.UpdateUsername(
username=username or ""
)
)
)

View File

@ -63,7 +63,7 @@ class InlineKeyboardButton(PyrogramType):
callback_game: CallbackGame = None): callback_game: CallbackGame = None):
super().__init__(None) super().__init__(None)
self.text = text self.text = str(text)
self.url = url self.url = url
self.callback_data = callback_data self.callback_data = callback_data
self.switch_inline_query = switch_inline_query self.switch_inline_query = switch_inline_query

View File

@ -46,7 +46,7 @@ class KeyboardButton(PyrogramType):
request_location: bool = None): request_location: bool = None):
super().__init__(None) super().__init__(None)
self.text = text self.text = str(text)
self.request_contact = request_contact self.request_contact = request_contact
self.request_location = request_location self.request_location = request_location

View File

@ -209,3 +209,14 @@ class Chat(PyrogramType):
parsed_chat.invite_link = full_chat.exported_invite.link parsed_chat.invite_link = full_chat.exported_invite.link
return parsed_chat return parsed_chat
@staticmethod
def _parse_chat(client, chat):
# A wrapper around each entity parser: User, Chat and Channel.
# Currently unused, might become useful in future.
if isinstance(chat, types.Chat):
return Chat._parse_chat_chat(client, chat)
elif isinstance(chat, types.User):
return Chat._parse_user_chat(client, chat)
else:
return Chat._parse_channel_chat(client, chat)