Merge branch 'layer-104' into develop
This commit is contained in:
commit
fb771441ed
@ -1,4 +1,4 @@
|
||||
// https://raw.githubusercontent.com/telegramdesktop/tdesktop/dev/Telegram/Resources/scheme.tl
|
||||
// https://github.com/telegramdesktop/tdesktop/blob/dev/Telegram/Resources/tl/api.tl
|
||||
|
||||
///////////////////////////////
|
||||
///////// Main application API
|
||||
@ -101,11 +101,11 @@ userStatusLastMonth#77ebc742 = UserStatus;
|
||||
chatEmpty#9ba2d800 id:int = Chat;
|
||||
chat#3bda1bde flags:# creator:flags.0?true kicked:flags.1?true left:flags.2?true deactivated:flags.5?true id:int title:string photo:ChatPhoto participants_count:int date:int version:int migrated_to:flags.6?InputChannel admin_rights:flags.14?ChatAdminRights default_banned_rights:flags.18?ChatBannedRights = Chat;
|
||||
chatForbidden#7328bdb id:int title:string = Chat;
|
||||
channel#4df30834 flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true has_link:flags.20?true has_geo:flags.21?true id:int access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int version:int restriction_reason:flags.9?string admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int = Chat;
|
||||
channel#4df30834 flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true has_link:flags.20?true has_geo:flags.21?true slowmode_enabled:flags.22?true id:int access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int version:int restriction_reason:flags.9?string admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int = Chat;
|
||||
channelForbidden#289da732 flags:# broadcast:flags.5?true megagroup:flags.8?true id:int access_hash:long title:string until_date:flags.16?int = Chat;
|
||||
|
||||
chatFull#1b7c9db3 flags:# can_set_username:flags.7?true id:int about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:flags.3?Vector<BotInfo> pinned_msg_id:flags.6?int folder_id:flags.11?int = ChatFull;
|
||||
channelFull#10916653 flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_view_stats:flags.12?true can_set_location:flags.16?true id:int about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?int migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?int location:flags.15?ChannelLocation pts:int = ChatFull;
|
||||
channelFull#2d895c74 flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_view_stats:flags.12?true can_set_location:flags.16?true id:int about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?int migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?int location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int pts:int = ChatFull;
|
||||
|
||||
chatParticipant#c8d7493e user_id:int inviter_id:int date:int = ChatParticipant;
|
||||
chatParticipantCreator#da13538a user_id:int = ChatParticipant;
|
||||
@ -172,11 +172,10 @@ photoStrippedSize#e0b0bc2e type:string bytes:bytes = PhotoSize;
|
||||
geoPointEmpty#1117dd5f = GeoPoint;
|
||||
geoPoint#296f104 long:double lat:double access_hash:long = GeoPoint;
|
||||
|
||||
auth.checkedPhone#811ea28e phone_registered:Bool = auth.CheckedPhone;
|
||||
|
||||
auth.sentCode#38faab5f flags:# phone_registered:flags.0?true type:auth.SentCodeType phone_code_hash:string next_type:flags.1?auth.CodeType timeout:flags.2?int terms_of_service:flags.3?help.TermsOfService = auth.SentCode;
|
||||
auth.sentCode#5e002502 flags:# type:auth.SentCodeType phone_code_hash:string next_type:flags.1?auth.CodeType timeout:flags.2?int = auth.SentCode;
|
||||
|
||||
auth.authorization#cd050916 flags:# tmp_sessions:flags.0?int user:User = auth.Authorization;
|
||||
auth.authorizationSignUpRequired#44747e9a flags:# terms_of_service:flags.0?help.TermsOfService = auth.Authorization;
|
||||
|
||||
auth.exportedAuthorization#df969c2d id:int bytes:bytes = auth.ExportedAuthorization;
|
||||
|
||||
@ -353,7 +352,7 @@ config#330b4067 flags:# phonecalls_enabled:flags.1?true default_p2p_contacts:fla
|
||||
|
||||
nearestDc#8e1a1775 country:string this_dc:int nearest_dc:int = NearestDc;
|
||||
|
||||
help.appUpdate#1da7158f flags:# popup:flags.0?true id:int version:string text:string entities:Vector<MessageEntity> document:flags.1?Document url:flags.2?string = help.AppUpdate;
|
||||
help.appUpdate#1da7158f flags:# can_not_skip:flags.0?true id:int version:string text:string entities:Vector<MessageEntity> document:flags.1?Document url:flags.2?string = help.AppUpdate;
|
||||
help.noAppUpdate#c45a6536 = help.AppUpdate;
|
||||
|
||||
help.inviteText#18cb9f78 message:string = help.InviteText;
|
||||
@ -496,6 +495,7 @@ chatInvite#dfc2f58e flags:# channel:flags.0?true broadcast:flags.1?true public:f
|
||||
inputStickerSetEmpty#ffb62b95 = InputStickerSet;
|
||||
inputStickerSetID#9de7a269 id:long access_hash:long = InputStickerSet;
|
||||
inputStickerSetShortName#861cc8a0 short_name:string = InputStickerSet;
|
||||
inputStickerSetAnimatedEmoji#28703c8 = InputStickerSet;
|
||||
|
||||
stickerSet#eeb46f27 flags:# archived:flags.1?true official:flags.2?true masks:flags.3?true animated:flags.5?true installed_date:flags.0?int id:long access_hash:long title:string short_name:string thumb:flags.4?PhotoSize thumb_dc_id:flags.4?int count:int hash:int = StickerSet;
|
||||
|
||||
@ -559,8 +559,8 @@ channelMessagesFilter#cd77d957 flags:# exclude_new_messages:flags.1?true ranges:
|
||||
|
||||
channelParticipant#15ebac1d user_id:int date:int = ChannelParticipant;
|
||||
channelParticipantSelf#a3289a6d user_id:int inviter_id:int date:int = ChannelParticipant;
|
||||
channelParticipantCreator#e3e2e1f9 user_id:int = ChannelParticipant;
|
||||
channelParticipantAdmin#5daa6e23 flags:# can_edit:flags.0?true self:flags.1?true user_id:int inviter_id:flags.1?int promoted_by:int date:int admin_rights:ChatAdminRights = ChannelParticipant;
|
||||
channelParticipantCreator#808d15a4 flags:# user_id:int rank:flags.0?string = ChannelParticipant;
|
||||
channelParticipantAdmin#ccbebbaf flags:# can_edit:flags.0?true self:flags.1?true user_id:int inviter_id:flags.1?int promoted_by:int date:int admin_rights:ChatAdminRights rank:flags.2?string = ChannelParticipant;
|
||||
channelParticipantBanned#1c0facaf flags:# left:flags.0?true user_id:int kicked_by:int date:int banned_rights:ChatBannedRights = ChannelParticipant;
|
||||
|
||||
channelParticipantsRecent#de3f3c79 = ChannelParticipantsFilter;
|
||||
@ -761,7 +761,7 @@ payments.paymentForm#3f56aea3 flags:# can_save_credentials:flags.2?true password
|
||||
payments.validatedRequestedInfo#d1451883 flags:# id:flags.0?string shipping_options:flags.1?Vector<ShippingOption> = payments.ValidatedRequestedInfo;
|
||||
|
||||
payments.paymentResult#4e5f810d updates:Updates = payments.PaymentResult;
|
||||
payments.paymentVerficationNeeded#6b56b921 url:string = payments.PaymentResult;
|
||||
payments.paymentVerificationNeeded#d8411139 url:string = payments.PaymentResult;
|
||||
|
||||
payments.paymentReceipt#500911e1 flags:# date:int bot_id:int invoice:Invoice provider_id:int info:flags.0?PaymentRequestedInfo shipping:flags.1?ShippingOption currency:string total_amount:long credentials_title:string users:Vector<User> = payments.PaymentReceipt;
|
||||
|
||||
@ -828,6 +828,7 @@ channelAdminLogEventActionDefaultBannedRights#2df5fc0a prev_banned_rights:ChatBa
|
||||
channelAdminLogEventActionStopPoll#8f079643 message:Message = ChannelAdminLogEventAction;
|
||||
channelAdminLogEventActionChangeLinkedChat#a26f881b prev_value:int new_value:int = ChannelAdminLogEventAction;
|
||||
channelAdminLogEventActionChangeLocation#e6b76ae prev_value:ChannelLocation new_value:ChannelLocation = ChannelAdminLogEventAction;
|
||||
channelAdminLogEventActionToggleSlowMode#53909779 prev_value:int new_value:int = ChannelAdminLogEventAction;
|
||||
|
||||
channelAdminLogEvent#3b5a3e40 id:long date:int user_id:int action:ChannelAdminLogEventAction = ChannelAdminLogEvent;
|
||||
|
||||
@ -1003,7 +1004,7 @@ inputWallPaperSlug#72091c80 slug:string = InputWallPaper;
|
||||
account.wallPapersNotModified#1c199183 = account.WallPapers;
|
||||
account.wallPapers#702b65a9 hash:int wallpapers:Vector<WallPaper> = account.WallPapers;
|
||||
|
||||
codeSettings#302f59f3 flags:# allow_flashcall:flags.0?true current_number:flags.1?true app_hash_persistent:flags.2?true app_hash:flags.3?string = CodeSettings;
|
||||
codeSettings#debebe83 flags:# allow_flashcall:flags.0?true current_number:flags.1?true allow_app_hash:flags.4?true = CodeSettings;
|
||||
|
||||
wallPaperSettings#a12f40b8 flags:# blur:flags.1?true motion:flags.2?true background_color:flags.0?int intensity:flags.3?int = WallPaperSettings;
|
||||
|
||||
@ -1050,7 +1051,7 @@ invokeWithMessagesRange#365275f2 {X:Type} range:MessageRange query:!X = X;
|
||||
invokeWithTakeout#aca9fd2e {X:Type} takeout_id:long query:!X = X;
|
||||
|
||||
auth.sendCode#a677244f phone_number:string api_id:int api_hash:string settings:CodeSettings = auth.SentCode;
|
||||
auth.signUp#1b067634 phone_number:string phone_code_hash:string phone_code:string first_name:string last_name:string = auth.Authorization;
|
||||
auth.signUp#80eee427 phone_number:string phone_code_hash:string first_name:string last_name:string = auth.Authorization;
|
||||
auth.signIn#bcd51581 phone_number:string phone_code_hash:string phone_code:string = auth.Authorization;
|
||||
auth.logOut#5717da40 = Bool;
|
||||
auth.resetAuthorizations#9fab0d1a = Bool;
|
||||
@ -1269,7 +1270,7 @@ photos.deletePhotos#87cf7f2f id:Vector<InputPhoto> = Vector<long>;
|
||||
photos.getUserPhotos#91cd32a8 user_id:InputUser offset:int max_id:long limit:int = photos.Photos;
|
||||
|
||||
upload.saveFilePart#b304a621 file_id:long file_part:int bytes:bytes = Bool;
|
||||
upload.getFile#e3a6cfb5 location:InputFileLocation offset:int limit:int = upload.File;
|
||||
upload.getFile#b15a9afc flags:# precise:flags.0?true location:InputFileLocation offset:int limit:int = upload.File;
|
||||
upload.saveBigFilePart#de7b673d file_id:long file_part:int file_total_parts:int bytes:bytes = Bool;
|
||||
upload.getWebFile#24e6818d location:InputWebFileLocation offset:int limit:int = upload.WebFile;
|
||||
upload.getCdnFile#2000bcc3 file_token:bytes offset:int limit:int = upload.CdnFile;
|
||||
@ -1307,7 +1308,7 @@ channels.getParticipant#546dd7a6 channel:InputChannel user_id:InputUser = channe
|
||||
channels.getChannels#a7f6bbb id:Vector<InputChannel> = messages.Chats;
|
||||
channels.getFullChannel#8736a09 channel:InputChannel = messages.ChatFull;
|
||||
channels.createChannel#3d5fb10f flags:# broadcast:flags.0?true megagroup:flags.1?true title:string about:string geo_point:flags.2?InputGeoPoint address:flags.2?string = Updates;
|
||||
channels.editAdmin#70f893ba channel:InputChannel user_id:InputUser admin_rights:ChatAdminRights = Updates;
|
||||
channels.editAdmin#d33c8902 channel:InputChannel user_id:InputUser admin_rights:ChatAdminRights rank:string = Updates;
|
||||
channels.editTitle#566decd0 channel:InputChannel title:string = Updates;
|
||||
channels.editPhoto#f12e57c9 channel:InputChannel photo:InputChatPhoto = Updates;
|
||||
channels.checkUsername#10e6bd2c channel:InputChannel username:string = Bool;
|
||||
@ -1330,6 +1331,7 @@ channels.getGroupsForDiscussion#f5dad378 = messages.Chats;
|
||||
channels.setDiscussionGroup#40582bb2 broadcast:InputChannel group:InputChannel = Bool;
|
||||
channels.editCreator#8f38cd1f channel:InputChannel user_id:InputUser password:InputCheckPasswordSRP = Updates;
|
||||
channels.editLocation#58e63f6d channel:InputChannel geo_point:InputGeoPoint address:string = Bool;
|
||||
channels.toggleSlowMode#edd49ef0 channel:InputChannel seconds:int = Updates;
|
||||
|
||||
bots.sendCustomRequest#aa2769ed custom_method:string params:DataJSON = DataJSON;
|
||||
bots.answerWebhookJSONQuery#e6213f4d query_id:long data:DataJSON = Bool;
|
||||
@ -1364,4 +1366,4 @@ langpack.getLanguage#6a596502 lang_pack:string lang_code:string = LangPackLangua
|
||||
folders.editPeerFolders#6847d0ab folder_peers:Vector<InputFolderPeer> = Updates;
|
||||
folders.deleteFolder#1c295881 folder_id:int = Updates;
|
||||
|
||||
// LAYER 103
|
||||
// LAYER 104
|
@ -128,10 +128,10 @@ def pyrogram_api():
|
||||
utilities="""
|
||||
Utilities
|
||||
start
|
||||
stop
|
||||
restart
|
||||
idle
|
||||
stop
|
||||
run
|
||||
restart
|
||||
add_handler
|
||||
remove_handler
|
||||
stop_transmission
|
||||
@ -249,6 +249,22 @@ def pyrogram_api():
|
||||
set_game_score
|
||||
get_game_high_scores
|
||||
""",
|
||||
authorization="""
|
||||
Authorization
|
||||
connect
|
||||
disconnect
|
||||
initialize
|
||||
terminate
|
||||
send_code
|
||||
resend_code
|
||||
sign_in
|
||||
sign_up
|
||||
get_password_hint
|
||||
check_password
|
||||
send_recovery_code
|
||||
recover_password
|
||||
accept_terms_of_service
|
||||
""",
|
||||
advanced="""
|
||||
Advanced
|
||||
send
|
||||
@ -349,6 +365,11 @@ def pyrogram_api():
|
||||
InputMessageContent
|
||||
InputMessageContent
|
||||
InputTextMessageContent
|
||||
""",
|
||||
authorization="""
|
||||
Authorization
|
||||
SentCode
|
||||
TermsOfService
|
||||
"""
|
||||
)
|
||||
|
||||
|
16
compiler/docs/template/methods.rst
vendored
16
compiler/docs/template/methods.rst
vendored
@ -106,10 +106,24 @@ Bots
|
||||
|
||||
{bots}
|
||||
|
||||
Authorization
|
||||
-------------
|
||||
|
||||
.. autosummary::
|
||||
:nosignatures:
|
||||
|
||||
{authorization}
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
|
||||
{authorization}
|
||||
|
||||
Advanced
|
||||
--------
|
||||
|
||||
Learn more about these methods at :doc:`Advanced Usage <../../topics/advanced-usage>`.
|
||||
Methods used only when dealing with the raw Telegram API.
|
||||
Learn more about how to use the raw API at :doc:`Advanced Usage <../../topics/advanced-usage>`.
|
||||
|
||||
.. autosummary::
|
||||
:nosignatures:
|
||||
|
13
compiler/docs/template/types.rst
vendored
13
compiler/docs/template/types.rst
vendored
@ -93,3 +93,16 @@ InputMessageContent
|
||||
:hidden:
|
||||
|
||||
{input_message_content}
|
||||
|
||||
Authorization
|
||||
-------------
|
||||
|
||||
.. autosummary::
|
||||
:nosignatures:
|
||||
|
||||
{authorization}
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
|
||||
{authorization}
|
@ -11,3 +11,4 @@ INTERDC_X_CALL_ERROR Telegram is having internal problems at DC{x}. Please try a
|
||||
INTERDC_X_CALL_RICH_ERROR Telegram is having internal problems at DC{x}. Please try again later
|
||||
FOLDER_DEAC_AUTOFIX_ALL Telegram is having internal problems. Please try again later
|
||||
MSGID_DECREASE_RETRY Telegram is having internal problems. Please try again later
|
||||
MEMBER_OCCUPY_PRIMARY_LOC_FAILED Telegram is having internal problems. Please try again later
|
|
@ -18,7 +18,6 @@
|
||||
|
||||
import logging
|
||||
import math
|
||||
import mimetypes
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
@ -45,12 +44,13 @@ from pyrogram.errors import (
|
||||
PhoneCodeExpired, PhoneCodeEmpty, SessionPasswordNeeded,
|
||||
PasswordHashInvalid, FloodWait, PeerIdInvalid, FirstnameInvalid, PhoneNumberBanned,
|
||||
VolumeLocNotFound, UserMigrate, ChannelPrivate, PhoneNumberOccupied,
|
||||
PasswordRecoveryNa, PasswordEmpty, AuthBytesInvalid
|
||||
)
|
||||
PasswordRecoveryNa, PasswordEmpty, AuthBytesInvalid,
|
||||
BadRequest)
|
||||
from pyrogram.session import Auth, Session
|
||||
from .ext import utils, Syncer, BaseClient, Dispatcher
|
||||
from .methods import Methods
|
||||
from .storage import Storage, FileStorage, MemoryStorage
|
||||
from .types import User, SentCode, TermsOfService
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@ -188,8 +188,6 @@ class Client(Methods, BaseClient):
|
||||
|
||||
"""
|
||||
|
||||
terms_of_service_displayed = False
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
session_name: Union[str, Storage],
|
||||
@ -280,70 +278,64 @@ class Client(Methods, BaseClient):
|
||||
self._proxy["enabled"] = bool(value.get("enabled", True))
|
||||
self._proxy.update(value)
|
||||
|
||||
def start(self):
|
||||
"""Start the client.
|
||||
def connect(self) -> bool:
|
||||
"""
|
||||
Connect the client to Telegram servers.
|
||||
|
||||
This method connects the client to Telegram and, in case of new sessions, automatically manages the full login
|
||||
process using an interactive prompt (by default).
|
||||
|
||||
Has no parameters.
|
||||
Returns:
|
||||
``bool``: On success, in case the passed-in session is authorized, True is returned. Otherwise, in case
|
||||
the session needs to be authorized, False is returned.
|
||||
|
||||
Raises:
|
||||
ConnectionError: In case you try to start an already started client.
|
||||
|
||||
Example:
|
||||
.. code-block:: python
|
||||
:emphasize-lines: 4
|
||||
|
||||
from pyrogram import Client
|
||||
|
||||
app = Client("my_account")
|
||||
app.start()
|
||||
|
||||
... # Call API methods
|
||||
|
||||
app.stop()
|
||||
ConnectionError: In case you try to connect an already connected client.
|
||||
"""
|
||||
if self.is_started:
|
||||
raise ConnectionError("Client has already been started")
|
||||
if self.is_connected:
|
||||
raise ConnectionError("Client is already connected")
|
||||
|
||||
self.load_config()
|
||||
self.load_session()
|
||||
self.load_plugins()
|
||||
|
||||
self.session = Session(self, self.storage.dc_id, self.storage.auth_key)
|
||||
|
||||
self.session.start()
|
||||
self.is_started = True
|
||||
|
||||
try:
|
||||
if self.storage.user_id is None:
|
||||
if self.bot_token is None:
|
||||
self.storage.is_bot = False
|
||||
self.authorize_user()
|
||||
else:
|
||||
self.storage.is_bot = True
|
||||
self.authorize_bot()
|
||||
self.is_connected = True
|
||||
|
||||
if not self.storage.is_bot:
|
||||
if self.takeout:
|
||||
self.takeout_id = self.send(functions.account.InitTakeoutSession()).id
|
||||
log.warning("Takeout session {} initiated".format(self.takeout_id))
|
||||
return bool(self.storage.user_id)
|
||||
|
||||
now = time.time()
|
||||
def disconnect(self):
|
||||
"""Disconnect the client from Telegram servers.
|
||||
|
||||
if abs(now - self.storage.date) > Client.OFFLINE_SLEEP:
|
||||
self.get_initial_dialogs()
|
||||
self.get_contacts()
|
||||
else:
|
||||
self.send(functions.messages.GetPinnedDialogs(folder_id=0))
|
||||
self.get_initial_dialogs_chunk()
|
||||
else:
|
||||
self.send(functions.updates.GetState())
|
||||
except Exception as e:
|
||||
self.is_started = False
|
||||
self.session.stop()
|
||||
raise e
|
||||
Raises:
|
||||
ConnectionError: In case you try to disconnect an already disconnected client or in case you try to
|
||||
disconnect a client that needs to be terminated first.
|
||||
"""
|
||||
if not self.is_connected:
|
||||
raise ConnectionError("Client is already disconnected")
|
||||
|
||||
if self.is_initialized:
|
||||
raise ConnectionError("Can't disconnect an initialized client")
|
||||
|
||||
self.session.stop()
|
||||
self.storage.close()
|
||||
self.is_connected = False
|
||||
|
||||
def initialize(self):
|
||||
"""Initialize the client by starting up workers.
|
||||
|
||||
This method will start updates and download workers.
|
||||
It will also load plugins and start the internal dispatcher.
|
||||
|
||||
Raises:
|
||||
ConnectionError: In case you try to initialize a disconnected client or in case you try to initialize an
|
||||
already initialized client.
|
||||
"""
|
||||
if not self.is_connected:
|
||||
raise ConnectionError("Can't initialize a disconnected client")
|
||||
|
||||
if self.is_initialized:
|
||||
raise ConnectionError("Client is already initialized")
|
||||
|
||||
self.load_plugins()
|
||||
|
||||
for i in range(self.UPDATES_WORKERS):
|
||||
self.updates_workers_list.append(
|
||||
@ -367,36 +359,21 @@ class Client(Methods, BaseClient):
|
||||
|
||||
self.dispatcher.start()
|
||||
|
||||
mimetypes.init()
|
||||
Syncer.add(self)
|
||||
|
||||
return self
|
||||
self.is_initialized = True
|
||||
|
||||
def stop(self):
|
||||
"""Stop the Client.
|
||||
def terminate(self):
|
||||
"""Terminate the client by shutting down workers.
|
||||
|
||||
This method disconnects the client from Telegram and stops the underlying tasks.
|
||||
|
||||
Has no parameters.
|
||||
This method does the opposite of :meth:`~Client.initialize`.
|
||||
It will stop the dispatcher and shut down updates and download workers.
|
||||
|
||||
Raises:
|
||||
ConnectionError: In case you try to stop an already stopped client.
|
||||
|
||||
Example:
|
||||
.. code-block:: python
|
||||
:emphasize-lines: 8
|
||||
|
||||
from pyrogram import Client
|
||||
|
||||
app = Client("my_account")
|
||||
app.start()
|
||||
|
||||
... # Call API methods
|
||||
|
||||
app.stop()
|
||||
ConnectionError: In case you try to terminate a client that is already terminated.
|
||||
"""
|
||||
if not self.is_started:
|
||||
raise ConnectionError("Client is already stopped")
|
||||
if not self.is_initialized:
|
||||
raise ConnectionError("Client is already terminated")
|
||||
|
||||
if self.takeout_id:
|
||||
self.send(functions.account.FinishTakeoutSession())
|
||||
@ -426,8 +403,490 @@ class Client(Methods, BaseClient):
|
||||
|
||||
self.media_sessions.clear()
|
||||
|
||||
self.is_started = False
|
||||
self.session.stop()
|
||||
self.is_initialized = False
|
||||
|
||||
def send_code(self, phone_number: str) -> SentCode:
|
||||
"""Send the confirmation code to the given phone number.
|
||||
|
||||
Parameters:
|
||||
phone_number (``str``):
|
||||
Phone number in international format (includes the country prefix).
|
||||
|
||||
Returns:
|
||||
:obj:`SentCode`: On success, an object containing information on the sent confirmation code is returned.
|
||||
|
||||
Raises:
|
||||
BadRequest: In case the phone number is invalid.
|
||||
"""
|
||||
phone_number = phone_number.strip(" +")
|
||||
|
||||
while True:
|
||||
try:
|
||||
r = self.send(
|
||||
functions.auth.SendCode(
|
||||
phone_number=phone_number,
|
||||
api_id=self.api_id,
|
||||
api_hash=self.api_hash,
|
||||
settings=types.CodeSettings()
|
||||
)
|
||||
)
|
||||
except (PhoneMigrate, NetworkMigrate) as e:
|
||||
self.session.stop()
|
||||
|
||||
self.storage.dc_id = e.x
|
||||
self.storage.auth_key = Auth(self, self.storage.dc_id).create()
|
||||
self.session = Session(self, self.storage.dc_id, self.storage.auth_key)
|
||||
|
||||
self.session.start()
|
||||
else:
|
||||
return SentCode._parse(r)
|
||||
|
||||
def resend_code(self, phone_number: str, phone_code_hash: str) -> SentCode:
|
||||
"""Re-send the confirmation code using a different type.
|
||||
|
||||
The type of the code to be re-sent is specified in the *next_type* attribute of the :obj:`SentCode` object
|
||||
returned by :meth:`send_code`.
|
||||
|
||||
Parameters:
|
||||
phone_number (``str``):
|
||||
Phone number in international format (includes the country prefix).
|
||||
|
||||
phone_code_hash (``str``):
|
||||
Confirmation code identifier.
|
||||
|
||||
Returns:
|
||||
:obj:`SentCode`: On success, an object containing information on the re-sent confirmation code is returned.
|
||||
|
||||
Raises:
|
||||
BadRequest: In case the arguments are invalid.
|
||||
"""
|
||||
phone_number = phone_number.strip(" +")
|
||||
|
||||
r = self.send(
|
||||
functions.auth.ResendCode(
|
||||
phone_number=phone_number,
|
||||
phone_code_hash=phone_code_hash
|
||||
)
|
||||
)
|
||||
|
||||
return SentCode._parse(r)
|
||||
|
||||
def sign_in(self, phone_number: str, phone_code_hash: str, phone_code: str) -> Union[User, TermsOfService, bool]:
|
||||
"""Authorize a user in Telegram with a valid confirmation code.
|
||||
|
||||
Parameters:
|
||||
phone_number (``str``):
|
||||
Phone number in international format (includes the country prefix).
|
||||
|
||||
phone_code_hash (``str``):
|
||||
Code identifier taken from the result of :meth:`~Client.send_code`.
|
||||
|
||||
phone_code (``str``):
|
||||
The valid confirmation code you received (either as Telegram message or as SMS in your phone number).
|
||||
|
||||
Returns:
|
||||
:obj:`User` | :obj:`TermsOfService` | bool: On success, in case the authorization completed, the user is
|
||||
returned. In case the phone number needs to be registered first AND the terms of services accepted (with
|
||||
:meth:`~Client.accept_terms_of_service`), an object containing them is returned. In case the phone number
|
||||
needs to be registered, but the terms of services don't need to be accepted, False is returned instead.
|
||||
|
||||
Raises:
|
||||
BadRequest: In case the arguments are invalid.
|
||||
SessionPasswordNeeded: In case a password is needed to sign in.
|
||||
"""
|
||||
phone_number = phone_number.strip(" +")
|
||||
|
||||
r = self.send(
|
||||
functions.auth.SignIn(
|
||||
phone_number=phone_number,
|
||||
phone_code_hash=phone_code_hash,
|
||||
phone_code=phone_code
|
||||
)
|
||||
)
|
||||
|
||||
if isinstance(r, types.auth.AuthorizationSignUpRequired):
|
||||
if r.terms_of_service:
|
||||
return TermsOfService._parse(terms_of_service=r.terms_of_service)
|
||||
|
||||
return False
|
||||
else:
|
||||
self.storage.user_id = r.user.id
|
||||
self.storage.is_bot = False
|
||||
|
||||
return User._parse(self, r.user)
|
||||
|
||||
def sign_up(self, phone_number: str, phone_code_hash: str, first_name: str, last_name: str = "") -> User:
|
||||
"""Register a new user in Telegram.
|
||||
|
||||
Parameters:
|
||||
phone_number (``str``):
|
||||
Phone number in international format (includes the country prefix).
|
||||
|
||||
phone_code_hash (``str``):
|
||||
Code identifier taken from the result of :meth:`~Client.send_code`.
|
||||
|
||||
first_name (``str``):
|
||||
New user first name.
|
||||
|
||||
last_name (``str``, *optional*):
|
||||
New user last name. Defaults to "" (empty string).
|
||||
|
||||
Returns:
|
||||
:obj:`User`: On success, the new registered user is returned.
|
||||
|
||||
Raises:
|
||||
BadRequest: In case the arguments are invalid.
|
||||
"""
|
||||
phone_number = phone_number.strip(" +")
|
||||
|
||||
r = self.send(
|
||||
functions.auth.SignUp(
|
||||
phone_number=phone_number,
|
||||
first_name=first_name,
|
||||
last_name=last_name,
|
||||
phone_code_hash=phone_code_hash
|
||||
)
|
||||
)
|
||||
|
||||
self.storage.user_id = r.user.id
|
||||
self.storage.is_bot = False
|
||||
|
||||
return User._parse(self, r.user)
|
||||
|
||||
def sign_in_bot(self, bot_token: str) -> User:
|
||||
"""Authorize a bot using its bot token generated by BotFather.
|
||||
|
||||
Parameters:
|
||||
bot_token (``str``):
|
||||
The bot token generated by BotFather
|
||||
|
||||
Returns:
|
||||
:obj:`User`: On success, the bot identity is return in form of a user object.
|
||||
|
||||
Raises:
|
||||
BadRequest: In case the bot token is invalid.
|
||||
"""
|
||||
while True:
|
||||
try:
|
||||
r = self.send(
|
||||
functions.auth.ImportBotAuthorization(
|
||||
flags=0,
|
||||
api_id=self.api_id,
|
||||
api_hash=self.api_hash,
|
||||
bot_auth_token=bot_token
|
||||
)
|
||||
)
|
||||
except UserMigrate as e:
|
||||
self.session.stop()
|
||||
|
||||
self.storage.dc_id = e.x
|
||||
self.storage.auth_key = Auth(self, self.storage.dc_id).create()
|
||||
self.session = Session(self, self.storage.dc_id, self.storage.auth_key)
|
||||
|
||||
self.session.start()
|
||||
else:
|
||||
self.storage.user_id = r.user.id
|
||||
self.storage.is_bot = True
|
||||
|
||||
return User._parse(self, r.user)
|
||||
|
||||
def get_password_hint(self) -> str:
|
||||
"""Get your Two-Step Verification password hint.
|
||||
|
||||
Returns:
|
||||
``str``: On success, the password hint as string is returned.
|
||||
"""
|
||||
return self.send(functions.account.GetPassword()).hint
|
||||
|
||||
def check_password(self, password: str) -> User:
|
||||
"""Check your Two-Step Verification password and log in.
|
||||
|
||||
Parameters:
|
||||
password (``str``):
|
||||
Your Two-Step Verification password.
|
||||
|
||||
Returns:
|
||||
:obj:`User`: On success, the authorized user is returned.
|
||||
|
||||
Raises:
|
||||
BadRequest: In case the password is invalid.
|
||||
"""
|
||||
r = self.send(
|
||||
functions.auth.CheckPassword(
|
||||
password=compute_check(
|
||||
self.send(functions.account.GetPassword()),
|
||||
password
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
self.storage.user_id = r.user.id
|
||||
self.storage.is_bot = False
|
||||
|
||||
return User._parse(self, r.user)
|
||||
|
||||
def send_recovery_code(self) -> str:
|
||||
"""Send a code to your email to recover your password.
|
||||
|
||||
Returns:
|
||||
``str``: On success, the hidden email pattern is returned and a recovery code is sent to that email.
|
||||
|
||||
Raises:
|
||||
BadRequest: In case no recovery email was set up.
|
||||
"""
|
||||
return self.send(
|
||||
functions.auth.RequestPasswordRecovery()
|
||||
).email_pattern
|
||||
|
||||
def recover_password(self, recovery_code: str) -> User:
|
||||
"""Recover your password with a recovery code and log in.
|
||||
|
||||
Parameters:
|
||||
recovery_code (``str``):
|
||||
The recovery code sent via email.
|
||||
|
||||
Returns:
|
||||
:obj:`User`: On success, the authorized user is returned and the Two-Step Verification password reset.
|
||||
|
||||
Raises:
|
||||
BadRequest: In case the recovery code is invalid.
|
||||
"""
|
||||
r = self.send(
|
||||
functions.auth.RecoverPassword(
|
||||
code=recovery_code
|
||||
)
|
||||
)
|
||||
|
||||
self.storage.user_id = r.user.id
|
||||
self.storage.is_bot = False
|
||||
|
||||
return User._parse(self, r.user)
|
||||
|
||||
def accept_terms_of_service(self, terms_of_service_id: str) -> bool:
|
||||
"""Accept the given terms of service.
|
||||
|
||||
Parameters:
|
||||
terms_of_service_id (``str``):
|
||||
The terms of service identifier.
|
||||
"""
|
||||
r = self.send(
|
||||
functions.help.AcceptTermsOfService(
|
||||
id=types.DataJSON(
|
||||
data=terms_of_service_id
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
assert r
|
||||
|
||||
return True
|
||||
|
||||
def authorize(self) -> User:
|
||||
if self.bot_token is not None:
|
||||
return self.sign_in_bot(self.bot_token)
|
||||
|
||||
while True:
|
||||
if self.phone_number is None:
|
||||
while True:
|
||||
value = input("Enter phone number or bot token: ")
|
||||
confirm = input("Is \"{}\" correct? (y/n): ".format(value))
|
||||
|
||||
if confirm in ("y", "1"):
|
||||
break
|
||||
elif confirm in ("n", "2"):
|
||||
continue
|
||||
|
||||
if ":" in value:
|
||||
self.bot_token = value
|
||||
return self.sign_in_bot(value)
|
||||
else:
|
||||
self.phone_number = value
|
||||
|
||||
try:
|
||||
sent_code = self.send_code(self.phone_number)
|
||||
except BadRequest as e:
|
||||
print(e.MESSAGE)
|
||||
self.phone_number = None
|
||||
except FloodWait as e:
|
||||
print(e.MESSAGE.format(x=e.x))
|
||||
time.sleep(e.x)
|
||||
except Exception as e:
|
||||
log.error(e, exc_info=True)
|
||||
else:
|
||||
break
|
||||
|
||||
if self.force_sms:
|
||||
sent_code = self.resend_code(self.phone_number, sent_code.phone_code_hash)
|
||||
|
||||
print("The confirmation code has been sent via {}".format(
|
||||
{
|
||||
"app": "Telegram app",
|
||||
"sms": "SMS",
|
||||
"call": "phone call",
|
||||
"flash_call": "phone flash call"
|
||||
}[sent_code.type]
|
||||
))
|
||||
|
||||
while True:
|
||||
if self.phone_code is None:
|
||||
self.phone_code = input("Enter confirmation code: ")
|
||||
|
||||
try:
|
||||
signed_in = self.sign_in(self.phone_number, sent_code.phone_code_hash, self.phone_code)
|
||||
except BadRequest as e:
|
||||
print(e.MESSAGE)
|
||||
self.phone_code = None
|
||||
except SessionPasswordNeeded as e:
|
||||
print(e.MESSAGE)
|
||||
|
||||
while True:
|
||||
print("Password hint: {}".format(self.get_password_hint()))
|
||||
|
||||
if self.password is None:
|
||||
self.password = input("Enter password (empty to recover): ")
|
||||
|
||||
try:
|
||||
if self.password == "":
|
||||
confirm = input("Confirm password recovery (y/n): ")
|
||||
|
||||
if confirm in ("y", "1"):
|
||||
email_pattern = self.send_recovery_code()
|
||||
print("The recovery code has been sent to {}".format(email_pattern))
|
||||
|
||||
while True:
|
||||
recovery_code = input("Enter recovery code: ")
|
||||
|
||||
try:
|
||||
return self.recover_password(recovery_code)
|
||||
except BadRequest as e:
|
||||
print(e.MESSAGE)
|
||||
except FloodWait as e:
|
||||
print(e.MESSAGE.format(x=e.x))
|
||||
time.sleep(e.x)
|
||||
except Exception as e:
|
||||
log.error(e, exc_info=True)
|
||||
raise
|
||||
|
||||
elif confirm in ("n", "2"):
|
||||
self.password = None
|
||||
else:
|
||||
return self.check_password(self.password)
|
||||
except BadRequest as e:
|
||||
print(e.MESSAGE)
|
||||
self.password = None
|
||||
except FloodWait as e:
|
||||
print(e.MESSAGE.format(x=e.x))
|
||||
time.sleep(e.x)
|
||||
except Exception as e:
|
||||
log.error(e, exc_info=True)
|
||||
raise
|
||||
except FloodWait as e:
|
||||
print(e.MESSAGE.format(x=e.x))
|
||||
time.sleep(e.x)
|
||||
except Exception as e:
|
||||
log.error(e, exc_info=True)
|
||||
else:
|
||||
break
|
||||
|
||||
if isinstance(signed_in, User):
|
||||
return signed_in
|
||||
|
||||
while True:
|
||||
self.first_name = input("Enter first name: ")
|
||||
self.last_name = input("Enter last name (empty to skip): ")
|
||||
|
||||
try:
|
||||
signed_up = self.sign_up(
|
||||
self.phone_number,
|
||||
sent_code.phone_code_hash,
|
||||
self.first_name,
|
||||
self.last_name
|
||||
)
|
||||
except BadRequest as e:
|
||||
print(e.MESSAGE)
|
||||
self.first_name = None
|
||||
self.last_name = None
|
||||
except FloodWait as e:
|
||||
print(e.MESSAGE.format(x=e.x))
|
||||
time.sleep(e.x)
|
||||
else:
|
||||
break
|
||||
|
||||
if isinstance(signed_in, TermsOfService):
|
||||
print("\n" + signed_in.text + "\n")
|
||||
self.accept_terms_of_service(signed_in.id)
|
||||
|
||||
return signed_up
|
||||
|
||||
def start(self):
|
||||
"""Start the client.
|
||||
|
||||
This method connects the client to Telegram and, in case of new sessions, automatically manages the full
|
||||
authorization process using an interactive prompt.
|
||||
|
||||
Returns:
|
||||
:obj:`Client`: The started client itself.
|
||||
|
||||
Raises:
|
||||
ConnectionError: In case you try to start an already started client.
|
||||
|
||||
Example:
|
||||
.. code-block:: python
|
||||
:emphasize-lines: 4
|
||||
|
||||
from pyrogram import Client
|
||||
|
||||
app = Client("my_account")
|
||||
app.start()
|
||||
|
||||
... # Call API methods
|
||||
|
||||
app.stop()
|
||||
"""
|
||||
is_authorized = self.connect()
|
||||
|
||||
try:
|
||||
if not is_authorized:
|
||||
self.authorize()
|
||||
|
||||
if not self.storage.is_bot and self.takeout:
|
||||
self.takeout_id = self.send(functions.account.InitTakeoutSession()).id
|
||||
log.warning("Takeout session {} initiated".format(self.takeout_id))
|
||||
|
||||
self.send(functions.updates.GetState())
|
||||
except Exception as e:
|
||||
self.disconnect()
|
||||
raise e
|
||||
else:
|
||||
self.initialize()
|
||||
return self
|
||||
|
||||
def stop(self):
|
||||
"""Stop the Client.
|
||||
|
||||
This method disconnects the client from Telegram and stops the underlying tasks.
|
||||
|
||||
Returns:
|
||||
:obj:`Client`: The stopped client itself.
|
||||
|
||||
Raises:
|
||||
ConnectionError: In case you try to stop an already stopped client.
|
||||
|
||||
Example:
|
||||
.. code-block:: python
|
||||
:emphasize-lines: 8
|
||||
|
||||
from pyrogram import Client
|
||||
|
||||
app = Client("my_account")
|
||||
app.start()
|
||||
|
||||
... # Call API methods
|
||||
|
||||
app.stop()
|
||||
"""
|
||||
self.terminate()
|
||||
self.disconnect()
|
||||
|
||||
return self
|
||||
|
||||
@ -437,7 +896,8 @@ class Client(Methods, BaseClient):
|
||||
This method will first call :meth:`~Client.stop` and then :meth:`~Client.start` in a row in order to restart
|
||||
a client using a single method.
|
||||
|
||||
Has no parameters.
|
||||
Returns:
|
||||
:obj:`Client`: The restarted client itself.
|
||||
|
||||
Raises:
|
||||
ConnectionError: In case you try to restart a stopped Client.
|
||||
@ -462,6 +922,8 @@ class Client(Methods, BaseClient):
|
||||
self.stop()
|
||||
self.start()
|
||||
|
||||
return self
|
||||
|
||||
@staticmethod
|
||||
def idle(stop_signals: tuple = (SIGINT, SIGTERM, SIGABRT)):
|
||||
"""Block the main script execution until a signal is received.
|
||||
@ -525,8 +987,6 @@ class Client(Methods, BaseClient):
|
||||
sequence. It makes running a client less verbose, but is not suitable in case you want to run more than one
|
||||
client in a single main script, since idle() will block after starting the own client.
|
||||
|
||||
Has no parameters.
|
||||
|
||||
Raises:
|
||||
ConnectionError: In case you try to run an already started client.
|
||||
|
||||
@ -628,8 +1088,6 @@ class Client(Methods, BaseClient):
|
||||
This method must be called inside a progress callback function in order to stop the transmission at the
|
||||
desired time. The progress callback is called every time a file chunk is uploaded/downloaded.
|
||||
|
||||
Has no parameters.
|
||||
|
||||
Example:
|
||||
.. code-block:: python
|
||||
:emphasize-lines: 9
|
||||
@ -656,8 +1114,6 @@ class Client(Methods, BaseClient):
|
||||
More detailed information about session strings can be found at the dedicated page of
|
||||
:doc:`Storage Engines <../../topics/storage-engines>`.
|
||||
|
||||
Has no parameters.
|
||||
|
||||
Returns:
|
||||
``str``: The session serialized into a printable, url-safe string.
|
||||
|
||||
@ -1211,8 +1667,8 @@ class Client(Methods, BaseClient):
|
||||
Raises:
|
||||
RPCError: In case of a Telegram RPC error.
|
||||
"""
|
||||
if not self.is_started:
|
||||
raise ConnectionError("Client has not been started")
|
||||
if not self.is_connected:
|
||||
raise ConnectionError("Client has not been started yet")
|
||||
|
||||
if self.no_updates:
|
||||
data = functions.InvokeWithoutUpdates(query=data)
|
||||
@ -1444,37 +1900,37 @@ class Client(Methods, BaseClient):
|
||||
log.warning('[{}] No plugin loaded from "{}"'.format(
|
||||
self.session_name, root))
|
||||
|
||||
def get_initial_dialogs_chunk(self, offset_date: int = 0):
|
||||
while True:
|
||||
try:
|
||||
r = self.send(
|
||||
functions.messages.GetDialogs(
|
||||
offset_date=offset_date,
|
||||
offset_id=0,
|
||||
offset_peer=types.InputPeerEmpty(),
|
||||
limit=self.DIALOGS_AT_ONCE,
|
||||
hash=0,
|
||||
exclude_pinned=True
|
||||
)
|
||||
)
|
||||
except FloodWait as e:
|
||||
log.warning("get_dialogs flood: waiting {} seconds".format(e.x))
|
||||
time.sleep(e.x)
|
||||
else:
|
||||
log.info("Total peers: {}".format(self.storage.peers_count))
|
||||
return r
|
||||
|
||||
def get_initial_dialogs(self):
|
||||
self.send(functions.messages.GetPinnedDialogs(folder_id=0))
|
||||
|
||||
dialogs = self.get_initial_dialogs_chunk()
|
||||
offset_date = utils.get_offset_date(dialogs)
|
||||
|
||||
while len(dialogs.dialogs) == self.DIALOGS_AT_ONCE:
|
||||
dialogs = self.get_initial_dialogs_chunk(offset_date)
|
||||
offset_date = utils.get_offset_date(dialogs)
|
||||
|
||||
self.get_initial_dialogs_chunk()
|
||||
# def get_initial_dialogs_chunk(self, offset_date: int = 0):
|
||||
# while True:
|
||||
# try:
|
||||
# r = self.send(
|
||||
# functions.messages.GetDialogs(
|
||||
# offset_date=offset_date,
|
||||
# offset_id=0,
|
||||
# offset_peer=types.InputPeerEmpty(),
|
||||
# limit=self.DIALOGS_AT_ONCE,
|
||||
# hash=0,
|
||||
# exclude_pinned=True
|
||||
# )
|
||||
# )
|
||||
# except FloodWait as e:
|
||||
# log.warning("get_dialogs flood: waiting {} seconds".format(e.x))
|
||||
# time.sleep(e.x)
|
||||
# else:
|
||||
# log.info("Total peers: {}".format(self.storage.peers_count))
|
||||
# return r
|
||||
#
|
||||
# def get_initial_dialogs(self):
|
||||
# self.send(functions.messages.GetPinnedDialogs(folder_id=0))
|
||||
#
|
||||
# dialogs = self.get_initial_dialogs_chunk()
|
||||
# offset_date = utils.get_offset_date(dialogs)
|
||||
#
|
||||
# while len(dialogs.dialogs) == self.DIALOGS_AT_ONCE:
|
||||
# dialogs = self.get_initial_dialogs_chunk(offset_date)
|
||||
# offset_date = utils.get_offset_date(dialogs)
|
||||
#
|
||||
# self.get_initial_dialogs_chunk()
|
||||
|
||||
def resolve_peer(self, peer_id: Union[int, str]):
|
||||
"""Get the InputPeer of a known peer id.
|
||||
@ -1495,9 +1951,11 @@ class Client(Methods, BaseClient):
|
||||
``InputPeer``: On success, the resolved peer id is returned in form of an InputPeer object.
|
||||
|
||||
Raises:
|
||||
RPCError: In case of a Telegram RPC error.
|
||||
KeyError: In case the peer doesn't exist in the internal database.
|
||||
"""
|
||||
if not self.is_connected:
|
||||
raise ConnectionError("Client has not been started yet")
|
||||
|
||||
try:
|
||||
return self.storage.get_peer_by_id(peer_id)
|
||||
except KeyError:
|
||||
@ -1678,7 +2136,7 @@ class Client(Methods, BaseClient):
|
||||
file_part += 1
|
||||
|
||||
if progress:
|
||||
progress(self, min(file_part * part_size, file_size), file_size, *progress_args)
|
||||
progress(min(file_part * part_size, file_size), file_size, *progress_args)
|
||||
except Client.StopTransmission:
|
||||
raise
|
||||
except Exception as e:
|
||||
@ -1813,7 +2271,6 @@ class Client(Methods, BaseClient):
|
||||
|
||||
if progress:
|
||||
progress(
|
||||
self,
|
||||
min(offset, file_size)
|
||||
if file_size != 0
|
||||
else offset,
|
||||
@ -1896,7 +2353,6 @@ class Client(Methods, BaseClient):
|
||||
|
||||
if progress:
|
||||
progress(
|
||||
self,
|
||||
min(offset, file_size)
|
||||
if file_size != 0
|
||||
else offset,
|
||||
|
@ -50,7 +50,6 @@ class BaseClient:
|
||||
PARENT_DIR = Path(sys.argv[0]).parent
|
||||
|
||||
INVITE_LINK_RE = re.compile(r"^(?:https?://)?(?:www\.)?(?:t(?:elegram)?\.(?:org|me|dog)/joinchat/)([\w-]+)$")
|
||||
BOT_TOKEN_RE = re.compile(r"^\d+:[\w-]+$")
|
||||
DIALOGS_AT_ONCE = 100
|
||||
UPDATES_WORKERS = 1
|
||||
DOWNLOAD_WORKERS = 1
|
||||
@ -103,7 +102,8 @@ class BaseClient:
|
||||
self.media_sessions = {}
|
||||
self.media_sessions_lock = Lock()
|
||||
|
||||
self.is_started = None
|
||||
self.is_connected = None
|
||||
self.is_initialized = None
|
||||
|
||||
self.takeout_id = None
|
||||
|
||||
|
@ -23,5 +23,6 @@ from .input_message_content import *
|
||||
from .list import List
|
||||
from .messages_and_media import *
|
||||
from .object import Object
|
||||
from .authorization import *
|
||||
from .update import *
|
||||
from .user_and_chats import *
|
||||
|
22
pyrogram/client/types/authorization/__init__.py
Normal file
22
pyrogram/client/types/authorization/__init__.py
Normal file
@ -0,0 +1,22 @@
|
||||
# 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 .terms_of_service import TermsOfService
|
||||
from .sent_code import SentCode
|
||||
|
||||
__all__ = ["TermsOfService", "SentCode"]
|
86
pyrogram/client/types/authorization/sent_code.py
Normal file
86
pyrogram/client/types/authorization/sent_code.py
Normal file
@ -0,0 +1,86 @@
|
||||
# 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 pyrogram.api import types
|
||||
from ..object import Object
|
||||
|
||||
|
||||
class SentCode(Object):
|
||||
"""Contains info on a sent confirmation code.
|
||||
|
||||
Parameters:
|
||||
type (``str``):
|
||||
Type of the current sent code.
|
||||
Can be *"app"* (code sent via Telegram), *"sms"* (code sent via SMS), *"call"* (code sent via voice call) or
|
||||
*"flash_call"* (code is in the last 5 digits of the caller's phone number).
|
||||
|
||||
phone_code_hash (``str``):
|
||||
Confirmation code identifier useful for the next authorization steps (either :meth:`~Client.sign_in` or
|
||||
:meth:`~Client.sign_up`).
|
||||
|
||||
next_type (``str``):
|
||||
Type of the next code to be sent with :meth:`~Client.resend_code`.
|
||||
Can be *"sms"* (code will be sent via SMS), *"call"* (code will be sent via voice call) or *"flash_call"*
|
||||
(code will be in the last 5 digits of caller's phone number).
|
||||
|
||||
timeout (``int``):
|
||||
Delay in seconds before calling :meth:`~Client.resend_code`.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, *,
|
||||
type: str,
|
||||
phone_code_hash: str,
|
||||
next_type: str = None,
|
||||
timeout: int = None
|
||||
):
|
||||
super().__init__()
|
||||
|
||||
self.type = type
|
||||
self.phone_code_hash = phone_code_hash
|
||||
self.next_type = next_type
|
||||
self.timeout = timeout
|
||||
|
||||
@staticmethod
|
||||
def _parse(sent_code: types.auth.SentCode) -> "SentCode":
|
||||
type = sent_code.type
|
||||
|
||||
if isinstance(type, types.auth.SentCodeTypeApp):
|
||||
type = "app"
|
||||
elif isinstance(type, types.auth.SentCodeTypeSms):
|
||||
type = "sms"
|
||||
elif isinstance(type, types.auth.SentCodeTypeCall):
|
||||
type = "call"
|
||||
elif isinstance(type, types.auth.SentCodeTypeFlashCall):
|
||||
type = "flash_call"
|
||||
|
||||
next_type = sent_code.next_type
|
||||
|
||||
if isinstance(next_type, types.auth.CodeTypeSms):
|
||||
next_type = "sms"
|
||||
elif isinstance(next_type, types.auth.CodeTypeCall):
|
||||
next_type = "call"
|
||||
elif isinstance(next_type, types.auth.CodeTypeFlashCall):
|
||||
next_type = "flash_call"
|
||||
|
||||
return SentCode(
|
||||
type=type,
|
||||
phone_code_hash=sent_code.phone_code_hash,
|
||||
next_type=next_type,
|
||||
timeout=sent_code.timeout
|
||||
)
|
56
pyrogram/client/types/authorization/terms_of_service.py
Normal file
56
pyrogram/client/types/authorization/terms_of_service.py
Normal file
@ -0,0 +1,56 @@
|
||||
# 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 List
|
||||
|
||||
from pyrogram.api import types
|
||||
from ..messages_and_media import MessageEntity
|
||||
from ..object import Object
|
||||
|
||||
|
||||
class TermsOfService(Object):
|
||||
"""Telegram's Terms of Service returned by :meth:`~Client.sign_in`.
|
||||
|
||||
Parameters:
|
||||
id (``str``):
|
||||
Terms of Service identifier.
|
||||
|
||||
text (``str``):
|
||||
Terms of Service text.
|
||||
|
||||
entities (List of :obj:`MessageEntity`):
|
||||
Special entities like URLs that appear in the text.
|
||||
"""
|
||||
|
||||
def __init__(self, *, id: str, text: str, entities: List[MessageEntity]):
|
||||
super().__init__()
|
||||
|
||||
self.id = id
|
||||
self.text = text
|
||||
self.entities = entities
|
||||
|
||||
@staticmethod
|
||||
def _parse(terms_of_service: types.help.TermsOfService) -> "TermsOfService":
|
||||
return TermsOfService(
|
||||
id=terms_of_service.id.data,
|
||||
text=terms_of_service.text,
|
||||
entities=[
|
||||
MessageEntity._parse(None, entity, {})
|
||||
for entity in terms_of_service.entities
|
||||
]
|
||||
)
|
@ -39,6 +39,7 @@ class Connection:
|
||||
|
||||
def __init__(self, dc_id: int, test_mode: bool, ipv6: bool, proxy: dict, mode: int = 3):
|
||||
self.dc_id = dc_id
|
||||
self.test_mode = test_mode
|
||||
self.ipv6 = ipv6
|
||||
self.proxy = proxy
|
||||
self.address = DataCenter(dc_id, test_mode, ipv6)
|
||||
@ -59,7 +60,8 @@ class Connection:
|
||||
self.connection.close()
|
||||
time.sleep(1)
|
||||
else:
|
||||
log.info("Connected! DC{} - IPv{} - {}".format(
|
||||
log.info("Connected! {} DC{} - IPv{} - {}".format(
|
||||
"Test" if self.test_mode else "Production",
|
||||
self.dc_id,
|
||||
"6" if self.ipv6 else "4",
|
||||
self.mode.__name__
|
||||
|
@ -26,11 +26,10 @@ from os import urandom
|
||||
from queue import Queue
|
||||
from threading import Event, Thread
|
||||
|
||||
from pyrogram.api.all import layer
|
||||
|
||||
import pyrogram
|
||||
from pyrogram import __copyright__, __license__, __version__
|
||||
from pyrogram.api import functions, types, core
|
||||
from pyrogram.api.all import layer
|
||||
from pyrogram.api.core import Message, TLObject, MsgContainer, Long, FutureSalt, Int
|
||||
from pyrogram.connection import Connection
|
||||
from pyrogram.crypto import AES, KDF
|
||||
@ -440,9 +439,9 @@ class Session:
|
||||
raise e from None
|
||||
|
||||
(log.warning if retries < 2 else log.info)(
|
||||
"{}: {} Retrying {}".format(
|
||||
"[{}] Retrying {} due to {}".format(
|
||||
Session.MAX_RETRIES - retries + 1,
|
||||
datetime.now(), type(data)))
|
||||
data.QUALNAME, e))
|
||||
|
||||
time.sleep(0.5)
|
||||
return self.send(data, retries - 1, timeout)
|
||||
|
Loading…
Reference in New Issue
Block a user