spluspy 2.1.6__tar.gz → 2.1.7__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {spluspy-2.1.6 → spluspy-2.1.7}/CHANGELOG.md +9 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/PKG-INFO +1 -1
- {spluspy-2.1.6 → spluspy-2.1.7}/pyproject.toml +1 -1
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/__version__.py +1 -1
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/client/account.py +1 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/client/auth.py +8 -2
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/client/chats.py +1 -1
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/client/dialogs.py +2 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/client/downloads.py +9 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/client/messages.py +1 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/client/telegrambaseclient.py +10 -4
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/client/updates.py +71 -37
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/client/uploads.py +4 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/client/users.py +15 -9
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/crypto/__init__.py +86 -84
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/crypto/aes.py +4 -2
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/crypto/libssl.py +138 -137
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/extensions/binaryreader.py +236 -231
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/extensions/html.py +208 -206
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/extensions/messagepacker.py +122 -119
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/network/connection/connection.py +2 -2
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/network/connection/websocket.py +12 -8
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/network/mtprotosender.py +54 -2
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/network/mtprotostate.py +4 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/sessions/sqlite.py +394 -393
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/custom/chatgetter.py +3 -2
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/custom/draft.py +194 -194
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/custom/message.py +4 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/custom/messagebutton.py +2 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/custom/sendergetter.py +2 -1
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/version.py +1 -1
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/afk.py +202 -201
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/client/chat_mixin.py +973 -954
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/client/client.py +26 -31
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/client/conversation.py +294 -295
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/client/media_mixin.py +531 -522
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/config.py +2 -2
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/middleware/base.py +2 -2
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/mirror.py +207 -204
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/network/client.py +12 -2
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/network/connection.py +6 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/network/http_client.py +355 -347
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/network/spluspy_adapter.py +495 -450
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/network/transport.py +252 -249
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/plugins/loader.py +3 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/storage/postgres.py +183 -183
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/storage/redis.py +140 -140
- spluspy-2.1.7/spluspy/utils/logger.py +282 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/utils/rate_limiter.py +1 -2
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/utils/resolver.py +372 -366
- spluspy-2.1.6/spluspy/utils/logger.py +0 -119
- {spluspy-2.1.6 → spluspy-2.1.7}/.gitignore +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/CONTRIBUTING.md +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/Dockerfile +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/LICENSE +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/MANIFEST.in +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/README.md +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/README.rst +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/examples/01_hello_bot.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/examples/02_echo_bot.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/examples/03_command_bot.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/examples/04_private_only.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/examples/05_group_only.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/examples/06_regex_bot.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/examples/07_user_filter.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/examples/08_combined_filters.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/examples/09_inline_keyboard.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/examples/10_reply_keyboard.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/examples/11_message_actions.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/examples/12_media_bot.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/examples/13_fsm_bot.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/examples/14_plugin_bot.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/examples/15_middleware_bot.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/examples/16_scheduler_bot.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/examples/17_conversation.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/examples/18_chat_management.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/examples/19_event_types.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/examples/20_sync_bot.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/examples/21_album_handler.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/examples/22_download_bot.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/examples/23_url_button.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/examples/24_string_session.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/examples/25_error_handling.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/examples/25_working_bot.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/examples/26_message_entities.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/examples/27_search_messages.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/examples/28_poll_bot.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/examples/29_dice_bot.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/examples/30_cache_bot.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/examples/31_reaction_bot.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/examples/32_mark_read_bot.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/examples/33_context_manager.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/examples/34_user_info.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/examples/35_chat_info.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/examples/36_forward_copy.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/examples/37_admin_bot.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/examples/38_media_filters.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/examples/39_status_bot.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/examples/40_delete_detection.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/examples/41_read_detection.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/examples/42_raw_events.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/examples/43_poll_handler.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/examples/44_reaction_handler.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/examples/45_typing_handler.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/examples/46_get_messages.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/examples/47_iter_messages.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/examples/48_get_members.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/examples/49_resolve_username.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/examples/50_invoke.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/setup.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/__init__.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/__init__.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/_updates/__init__.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/_updates/entitycache.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/_updates/messagebox.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/_updates/session.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/client/__init__.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/client/bots.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/client/buttons.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/client/messageparse.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/client/soroushclient.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/crypto/aesctr.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/crypto/authkey.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/crypto/cdndecrypter.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/crypto/factorization.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/crypto/fast_aes.pyx +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/crypto/fast_rsa.pyx +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/crypto/rsa.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/custom.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/errors/__init__.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/errors/common.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/errors/rpcbaseerrors.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/errors/rpcerrorlist.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/events/__init__.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/events/album.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/events/callbackquery.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/events/chataction.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/events/common.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/events/inlinequery.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/events/messagedeleted.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/events/messageedited.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/events/messageread.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/events/newmessage.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/events/raw.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/events/userupdate.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/extensions/__init__.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/extensions/markdown.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/functions.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/helpers.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/hints.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/network/__init__.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/network/authenticator.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/network/connection/__init__.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/network/connection/http.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/network/connection/tcpabridged.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/network/connection/tcpfull.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/network/connection/tcpintermediate.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/network/connection/tcpmtproxy.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/network/connection/tcpobfuscated.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/network/mtprotoplainsender.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/network/requeststate.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/password.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/requestiter.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/sessions/__init__.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/sessions/abstract.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/sessions/memory.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/sessions/string.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/sync.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/__init__.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/alltlobjects.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/core/__init__.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/core/gzippacked.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/core/messagecontainer.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/core/rpcresult.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/core/tlmessage.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/custom/__init__.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/custom/adminlogevent.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/custom/button.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/custom/conversation.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/custom/dialog.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/custom/file.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/custom/forward.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/custom/inlinebuilder.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/custom/inlineresult.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/custom/inlineresults.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/custom/inputsizedfile.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/custom/participantpermissions.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/custom/qrlogin.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/custom/types.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/functions/__init__.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/functions/account.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/functions/auth.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/functions/bots.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/functions/channels.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/functions/chatlists.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/functions/conference.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/functions/contacts.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/functions/folders.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/functions/help.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/functions/langpack.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/functions/messages.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/functions/payments.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/functions/phone.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/functions/photos.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/functions/premium.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/functions/stats.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/functions/stories.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/functions/thirdParty.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/functions/updates.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/functions/upload.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/functions/users.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/patched/__init__.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/tlobject.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/types/__init__.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/types/account.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/types/auth.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/types/bots.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/types/channels.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/types/chatlists.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/types/conference.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/types/contacts.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/types/help.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/types/messages.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/types/payments.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/types/phone.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/types/photos.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/types/premium.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/types/stats.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/types/stickers.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/types/storage.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/types/stories.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/types/thirdParty.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/types/update.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/types/updates.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/types/upload.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/tl/types/users.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/types.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/_engine/utils.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/admin.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/cli.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/client/__init__.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/compat.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/errors/__init__.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/errors/exceptions.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/events/__init__.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/events/album.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/events/base.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/events/callback.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/events/chat_action.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/events/edited.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/events/inline.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/events/message.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/events/message_deleted.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/events/message_read.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/events/user_update.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/filters/__init__.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/filters/filters.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/fsm/__init__.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/fsm/state.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/middleware/__init__.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/middleware/rate_limit.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/models/__init__.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/models/bot.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/models/chat.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/models/enums.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/models/media.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/models/message.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/models/objects.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/models/user.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/network/__init__.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/network/mtproto.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/network/protocols.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/plugins/__init__.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/py.typed +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/scheduler/__init__.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/scheduler/scheduler.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/session/__init__.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/session/base.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/session/memory.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/session/sqlite.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/session/string.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/storage/__init__.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/storage/base.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/storage/entity_cache.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/storage/memory.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/storage/sqlite.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/sync/__init__.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/sync/client.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/utils/__init__.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/utils/batch.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/utils/cache.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/utils/chat_lock.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/utils/file_transfer.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/utils/helpers.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/spluspy/utils/version_check.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/tests/__init__.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/tests/test_client.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/tests/test_filters.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/tests/test_fsm.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/tests/test_fsm_enhanced.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/tests/test_middleware.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/tests/test_rate_limiter.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/tests/test_resolver.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/tests/test_scheduler.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/tests/test_session.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/tests/test_storage.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/tests/test_types.py +0 -0
- {spluspy-2.1.6 → spluspy-2.1.7}/tests/test_utils.py +0 -0
|
@@ -5,6 +5,15 @@ All notable changes to SplusPy will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
7
7
|
|
|
8
|
+
## [2.1.7] - 2026-07-15
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- Fixed critical reconnection bug: `_reconnect()` now resets `_user_connected` flag so send/recv loops restart after connection drop
|
|
12
|
+
- Fixed `_connection_monitor` checking wrong attribute name (`_update_task` → `_updates_handle`)
|
|
13
|
+
- Added keepalive loop monitoring to `_connection_monitor`
|
|
14
|
+
- Added clean-exit detection for update loop in connection monitor
|
|
15
|
+
- Wrapped connection monitor body in try/except to prevent silent monitor death
|
|
16
|
+
|
|
8
17
|
## [2.1.3] - 2026-07-13
|
|
9
18
|
|
|
10
19
|
### Fixed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: spluspy
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.7
|
|
4
4
|
Summary: A modern async Python library for interacting with Soroush Plus
|
|
5
5
|
Project-URL: Homepage, https://github.com/Itskillmaster/spluspy
|
|
6
6
|
Project-URL: Documentation, https://github.com/Itskillmaster/spluspy#readme
|
|
@@ -160,7 +160,7 @@ class AuthMethods:
|
|
|
160
160
|
'you are accidentally reusing an existing session'
|
|
161
161
|
)
|
|
162
162
|
except Exception as e:
|
|
163
|
-
|
|
163
|
+
_log.debug('Failed to verify existing session user: %s', e)
|
|
164
164
|
|
|
165
165
|
return self
|
|
166
166
|
|
|
@@ -352,6 +352,7 @@ class AuthMethods:
|
|
|
352
352
|
except Exception as e:
|
|
353
353
|
# get_me may fail on Soroush (GetUsersRequest returns 500).
|
|
354
354
|
# Check authorization via GetState instead.
|
|
355
|
+
_log.debug('get_me failed, checking auth state: %s', e)
|
|
355
356
|
if await self.is_user_authorized():
|
|
356
357
|
return None
|
|
357
358
|
if me:
|
|
@@ -399,6 +400,7 @@ class AuthMethods:
|
|
|
399
400
|
try:
|
|
400
401
|
me = await self.get_me()
|
|
401
402
|
except Exception as e:
|
|
403
|
+
_log.debug('get_me failed after auth check: %s', e)
|
|
402
404
|
me = None
|
|
403
405
|
if me:
|
|
404
406
|
return await self._on_login(me)
|
|
@@ -419,7 +421,7 @@ class AuthMethods:
|
|
|
419
421
|
int(state.date.timestamp()), state.seq, 0),
|
|
420
422
|
[])
|
|
421
423
|
except Exception as e:
|
|
422
|
-
|
|
424
|
+
_log.debug('Failed to initialize message box during login: %s', e)
|
|
423
425
|
return None
|
|
424
426
|
raise
|
|
425
427
|
|
|
@@ -535,6 +537,9 @@ class AuthMethods:
|
|
|
535
537
|
if _retry_count > 2:
|
|
536
538
|
raise
|
|
537
539
|
self._phone_code_hash.pop(phone, None)
|
|
540
|
+
self._log[__name__].debug(
|
|
541
|
+
"Phone code expired in ResendCodeRequest, requesting a new code"
|
|
542
|
+
)
|
|
538
543
|
return await self.send_code_request(
|
|
539
544
|
phone, force_sms=False, _retry_count=_retry_count+1)
|
|
540
545
|
|
|
@@ -603,6 +608,7 @@ class AuthMethods:
|
|
|
603
608
|
try:
|
|
604
609
|
await self(functions.auth.LogOutRequest())
|
|
605
610
|
except errors.RPCError as e:
|
|
611
|
+
_log.debug('Logout failed: %s', e)
|
|
606
612
|
return False
|
|
607
613
|
|
|
608
614
|
self._mb_entity_cache.set_self_user(None, None, None)
|
|
@@ -88,7 +88,7 @@ class _ChatAction:
|
|
|
88
88
|
await self._client(self._request)
|
|
89
89
|
await asyncio.sleep(self._delay)
|
|
90
90
|
except ConnectionError as e:
|
|
91
|
-
|
|
91
|
+
_log.debug('Connection error during typing action: %s', e)
|
|
92
92
|
except asyncio.CancelledError:
|
|
93
93
|
if self._auto_cancel:
|
|
94
94
|
await self._client(functions.messages.SetTypingRequest(
|
|
@@ -467,6 +467,7 @@ class DialogMethods:
|
|
|
467
467
|
))
|
|
468
468
|
except errors.PeerIdInvalidError as e:
|
|
469
469
|
# Happens if we didn't have the deactivated information
|
|
470
|
+
_log.debug('PeerIdInvalidError when deleting chat: %s', e)
|
|
470
471
|
result = None
|
|
471
472
|
else:
|
|
472
473
|
result = None
|
|
@@ -563,6 +564,7 @@ class DialogMethods:
|
|
|
563
564
|
await self.delete_dialog(entity, revoke=revoke)
|
|
564
565
|
result["success"] += 1
|
|
565
566
|
result["details"].append({"id": user_id, "title": title, "status": "deleted"})
|
|
567
|
+
_log.debug("delete_all_private: deleted %s (%d)", title, user_id)
|
|
566
568
|
except Exception as exc:
|
|
567
569
|
result["failed"] += 1
|
|
568
570
|
result["details"].append({"id": user_id, "title": title, "status": "failed"})
|
|
@@ -108,10 +108,12 @@ class _DirectDownloadIter(RequestIter):
|
|
|
108
108
|
raise
|
|
109
109
|
|
|
110
110
|
self._timed_out = True
|
|
111
|
+
self.client._log[__name__].debug('Got timeout while downloading file, retrying once')
|
|
111
112
|
await asyncio.sleep(TIMED_OUT_SLEEP)
|
|
112
113
|
return await self._request()
|
|
113
114
|
|
|
114
115
|
except errors.FileMigrateError as e:
|
|
116
|
+
self.client._log[__name__].debug('File lives in another DC')
|
|
115
117
|
self._sender = await self.client._borrow_exported_sender(e.new_dc)
|
|
116
118
|
self._exported = True
|
|
117
119
|
return await self._request()
|
|
@@ -123,6 +125,7 @@ class _DirectDownloadIter(RequestIter):
|
|
|
123
125
|
or self.request.location.thumb_size != '':
|
|
124
126
|
raise
|
|
125
127
|
|
|
128
|
+
self.client._log[__name__].debug('File ref expired during download; refetching message')
|
|
126
129
|
chat, msg_id = self._msg_data
|
|
127
130
|
msg = await self.client.get_messages(chat, ids=msg_id)
|
|
128
131
|
|
|
@@ -583,6 +586,7 @@ class DownloadMethods:
|
|
|
583
586
|
if in_memory:
|
|
584
587
|
return f.getvalue()
|
|
585
588
|
except _CdnRedirect as e:
|
|
589
|
+
self._log[__name__].debug('FileCdnRedirect to CDN data center %s', e.cdn_redirect.dc_id)
|
|
586
590
|
return await self._download_file(
|
|
587
591
|
input_location=input_location,
|
|
588
592
|
file=file,
|
|
@@ -750,8 +754,12 @@ class DownloadMethods:
|
|
|
750
754
|
and stride % MIN_CHUNK_SIZE == 0 \
|
|
751
755
|
and (limit is None or offset % limit == 0):
|
|
752
756
|
cls = _DirectDownloadIter
|
|
757
|
+
self._log[__name__].debug('Starting direct file download in chunks of '
|
|
758
|
+
'%d at %d, stride %d', request_size, offset, stride)
|
|
753
759
|
else:
|
|
754
760
|
cls = _GenericDownloadIter
|
|
761
|
+
self._log[__name__].debug('Starting indirect file download in chunks of '
|
|
762
|
+
'%d at %d, stride %d', request_size, offset, stride)
|
|
755
763
|
|
|
756
764
|
return cls(
|
|
757
765
|
self,
|
|
@@ -1056,6 +1064,7 @@ class DownloadMethods:
|
|
|
1056
1064
|
if x and not isreserved(x)
|
|
1057
1065
|
)
|
|
1058
1066
|
except StopIteration:
|
|
1067
|
+
_log.debug('No valid filename found in possible_names')
|
|
1059
1068
|
name = None
|
|
1060
1069
|
|
|
1061
1070
|
if not name:
|
|
@@ -311,6 +311,7 @@ class _IDsIter(RequestIter):
|
|
|
311
311
|
functions.channels.GetMessagesRequest(self._entity, ids))
|
|
312
312
|
except errors.MessageIdsEmptyError as e:
|
|
313
313
|
# All IDs were invalid, use a dummy result
|
|
314
|
+
_log.debug('MessageIdsEmptyError for channel messages: %s', e)
|
|
314
315
|
r = types.messages.MessagesNotModified(len(ids))
|
|
315
316
|
else:
|
|
316
317
|
r = await self.client(functions.messages.GetMessagesRequest(ids))
|
|
@@ -394,7 +394,6 @@ class SoroushPlusBaseClient(abc.ABC):
|
|
|
394
394
|
# Used for non-sequential updates, in order to terminate all pending tasks on disconnect.
|
|
395
395
|
self._sequential_updates = sequential_updates
|
|
396
396
|
self._event_handler_tasks = set()
|
|
397
|
-
self._max_handler_tasks = 50 # Limit concurrent handler tasks
|
|
398
397
|
|
|
399
398
|
self._authorized = None # None = unknown, False = no, True = yes
|
|
400
399
|
|
|
@@ -575,7 +574,7 @@ class SoroushPlusBaseClient(abc.ABC):
|
|
|
575
574
|
self_user = await utils.maybe_async(self.session.get_input_entity(self_id))
|
|
576
575
|
self._mb_entity_cache.set_self_user(self_id, None, self_user.access_hash)
|
|
577
576
|
except ValueError as e:
|
|
578
|
-
|
|
577
|
+
_base_log.debug('Could not load self user from session: %s', e)
|
|
579
578
|
|
|
580
579
|
if self._catch_up:
|
|
581
580
|
ss = SessionState(0, 0, False, 0, 0, 0, 0, None)
|
|
@@ -611,7 +610,7 @@ class SoroushPlusBaseClient(abc.ABC):
|
|
|
611
610
|
# Soroush server sends config types with custom constructor IDs
|
|
612
611
|
# and different field layouts than standard SoroushPlus. The response
|
|
613
612
|
# is not used anyway, so we can safely ignore parse errors.
|
|
614
|
-
|
|
613
|
+
_base_log.debug('Ignored config parse error during init: %s', e)
|
|
615
614
|
|
|
616
615
|
# Don't call get_me() here — on Soroush the auth key is not
|
|
617
616
|
# registered until after login, and GetUsersRequest (used by
|
|
@@ -672,7 +671,7 @@ class SoroushPlusBaseClient(abc.ABC):
|
|
|
672
671
|
# "Event loop stopped before Future completed."
|
|
673
672
|
#
|
|
674
673
|
# However, it doesn't really make a lot of sense.
|
|
675
|
-
|
|
674
|
+
_base_log.debug('RuntimeError during disconnect: %s', e)
|
|
676
675
|
|
|
677
676
|
def set_proxy(self: 'SoroushClient', proxy: typing.Union[tuple, dict]):
|
|
678
677
|
"""
|
|
@@ -775,6 +774,7 @@ class SoroushPlusBaseClient(abc.ABC):
|
|
|
775
774
|
"""
|
|
776
775
|
Permanently switches the current connection to the new data center.
|
|
777
776
|
"""
|
|
777
|
+
self._log[__name__].debug('Reconnecting to new data center %s', new_dc)
|
|
778
778
|
dc = await self._get_dc(new_dc)
|
|
779
779
|
|
|
780
780
|
await utils.maybe_async(self.session.set_dc(dc.id, dc.ip_address, dc.port))
|
|
@@ -884,6 +884,7 @@ class SoroushPlusBaseClient(abc.ABC):
|
|
|
884
884
|
proxy=self._proxy,
|
|
885
885
|
local_addr=self._local_addr
|
|
886
886
|
))
|
|
887
|
+
self._log[__name__].debug('Exporting auth for new borrowed sender in %s', dc)
|
|
887
888
|
auth = await self(functions.auth.ExportAuthorizationRequest(dc_id))
|
|
888
889
|
self._init_request.query = functions.auth.ImportAuthorizationRequest(id=auth.id, bytes=auth.bytes)
|
|
889
890
|
req = functions.InvokeWithLayerRequest(LAYER, self._init_request)
|
|
@@ -899,6 +900,7 @@ class SoroushPlusBaseClient(abc.ABC):
|
|
|
899
900
|
Once its job is over it should be `_return_exported_sender`.
|
|
900
901
|
"""
|
|
901
902
|
async with self._borrow_sender_lock:
|
|
903
|
+
self._log[__name__].debug('Borrowing sender for dc_id %d', dc_id)
|
|
902
904
|
state, sender = self._borrowed_senders.get(dc_id, (None, None))
|
|
903
905
|
|
|
904
906
|
if state is None:
|
|
@@ -927,6 +929,7 @@ class SoroushPlusBaseClient(abc.ABC):
|
|
|
927
929
|
been returned, the sender is cleanly disconnected.
|
|
928
930
|
"""
|
|
929
931
|
async with self._borrow_sender_lock:
|
|
932
|
+
self._log[__name__].debug('Returning borrowed sender for dc_id %d', sender.dc_id)
|
|
930
933
|
state, _ = self._borrowed_senders[sender.dc_id]
|
|
931
934
|
state.add_return()
|
|
932
935
|
|
|
@@ -937,6 +940,8 @@ class SoroushPlusBaseClient(abc.ABC):
|
|
|
937
940
|
async with self._borrow_sender_lock:
|
|
938
941
|
for dc_id, (state, sender) in self._borrowed_senders.items():
|
|
939
942
|
if state.should_disconnect():
|
|
943
|
+
self._log[__name__].debug(
|
|
944
|
+
'Disconnecting borrowed sender for DC %d', dc_id)
|
|
940
945
|
|
|
941
946
|
# Disconnect should never raise
|
|
942
947
|
await sender.disconnect()
|
|
@@ -951,6 +956,7 @@ class SoroushPlusBaseClient(abc.ABC):
|
|
|
951
956
|
await utils.maybe_async(session.set_dc(dc.id, dc.ip_address, dc.port))
|
|
952
957
|
self._exported_sessions[cdn_redirect.dc_id] = session
|
|
953
958
|
|
|
959
|
+
self._log[__name__].debug('Creating new CDN client')
|
|
954
960
|
client = self.__class__(
|
|
955
961
|
session, self.api_id, self.api_hash,
|
|
956
962
|
proxy=self._proxy,
|
|
@@ -4,6 +4,7 @@ import itertools
|
|
|
4
4
|
import random
|
|
5
5
|
import sys
|
|
6
6
|
import time
|
|
7
|
+
import traceback
|
|
7
8
|
import typing
|
|
8
9
|
import logging
|
|
9
10
|
import warnings
|
|
@@ -283,11 +284,14 @@ class UpdateMethods:
|
|
|
283
284
|
if self._sequential_updates:
|
|
284
285
|
await self._dispatch_update(updates_to_dispatch.popleft())
|
|
285
286
|
else:
|
|
286
|
-
#
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
287
|
+
# Spawn one task per loop iteration instead of draining the entire
|
|
288
|
+
# deque in a tight while-loop. Draining all at once creates O(n) tasks
|
|
289
|
+
# before the event loop gets a chance to run any of them, causing task
|
|
290
|
+
# count (and scheduling overhead) to grow unboundedly under high load.
|
|
291
|
+
# TODO if _dispatch_update fails for whatever reason, it's not logged! this should be fixed
|
|
292
|
+
task = self.loop.create_task(self._dispatch_update(updates_to_dispatch.popleft()))
|
|
293
|
+
self._event_handler_tasks.add(task)
|
|
294
|
+
task.add_done_callback(self._event_handler_tasks.discard)
|
|
291
295
|
|
|
292
296
|
continue
|
|
293
297
|
|
|
@@ -295,6 +299,7 @@ class UpdateMethods:
|
|
|
295
299
|
|
|
296
300
|
get_diff = self._message_box.get_difference()
|
|
297
301
|
if get_diff:
|
|
302
|
+
self._log[__name__].debug('Getting difference for account updates')
|
|
298
303
|
try:
|
|
299
304
|
diff = await self(get_diff)
|
|
300
305
|
except (
|
|
@@ -304,10 +309,12 @@ class UpdateMethods:
|
|
|
304
309
|
ValueError
|
|
305
310
|
) as e:
|
|
306
311
|
# SoroushPlus is having issues
|
|
312
|
+
self._log[__name__].debug('Cannot get difference since SoroushPlus is having issues: %s', type(e).__name__)
|
|
307
313
|
self._message_box.end_difference()
|
|
308
314
|
continue
|
|
309
315
|
except (errors.UnauthorizedError, errors.AuthKeyError) as e:
|
|
310
316
|
# Not logged in or broken authorization key, can't get difference
|
|
317
|
+
self._log[__name__].debug('Cannot get difference since the account is not logged in: %s', type(e).__name__)
|
|
311
318
|
self._message_box.end_difference()
|
|
312
319
|
if was_once_logged_in:
|
|
313
320
|
self._updates_error = e
|
|
@@ -334,16 +341,21 @@ class UpdateMethods:
|
|
|
334
341
|
# Network is likely down, but it's unclear for how long.
|
|
335
342
|
# If disconnect is called this task will be cancelled along with the sleep.
|
|
336
343
|
# If disconnect is not called, getting difference should be retried after a few seconds.
|
|
344
|
+
self._log[__name__].debug('Cannot get difference since the network is down: %s: %s', type(e).__name__, e)
|
|
337
345
|
self._message_box.end_difference()
|
|
338
346
|
await asyncio.sleep(5)
|
|
339
347
|
continue
|
|
340
348
|
updates, users, chats = self._message_box.apply_difference(diff, self._mb_entity_cache)
|
|
349
|
+
if updates:
|
|
350
|
+
self._log[__name__].debug('Got difference for account updates')
|
|
351
|
+
|
|
341
352
|
_preprocess_updates = await self._preprocess_updates(updates, users, chats)
|
|
342
353
|
updates_to_dispatch.extend(_preprocess_updates)
|
|
343
354
|
continue
|
|
344
355
|
|
|
345
356
|
get_diff = self._message_box.get_channel_difference(self._mb_entity_cache)
|
|
346
357
|
if get_diff:
|
|
358
|
+
self._log[__name__].debug('Getting difference for channel %s updates', get_diff.channel.channel_id)
|
|
347
359
|
try:
|
|
348
360
|
diff = await self(get_diff)
|
|
349
361
|
except (errors.UnauthorizedError, errors.AuthKeyError) as e:
|
|
@@ -414,6 +426,10 @@ class UpdateMethods:
|
|
|
414
426
|
# Timeout triggered a get difference, but we have been banned in the channel since then.
|
|
415
427
|
# Because we can no longer fetch updates from this channel, we should stop keeping track
|
|
416
428
|
# of it entirely.
|
|
429
|
+
self._log[__name__].debug(
|
|
430
|
+
'Account is now banned in %d so we can no longer fetch updates from it',
|
|
431
|
+
get_diff.channel.channel_id
|
|
432
|
+
)
|
|
417
433
|
self._message_box.end_channel_difference(
|
|
418
434
|
get_diff,
|
|
419
435
|
PrematureEndReason.BANNED,
|
|
@@ -434,6 +450,10 @@ class UpdateMethods:
|
|
|
434
450
|
)
|
|
435
451
|
continue
|
|
436
452
|
except OSError as e:
|
|
453
|
+
self._log[__name__].debug(
|
|
454
|
+
'Cannot get difference for channel %d since the network is down: %s: %s',
|
|
455
|
+
get_diff.channel.channel_id, type(e).__name__, e
|
|
456
|
+
)
|
|
437
457
|
self._message_box.end_channel_difference(
|
|
438
458
|
get_diff,
|
|
439
459
|
PrematureEndReason.TEMPORARY_SERVER_ISSUES,
|
|
@@ -443,6 +463,9 @@ class UpdateMethods:
|
|
|
443
463
|
continue
|
|
444
464
|
|
|
445
465
|
updates, users, chats = self._message_box.apply_channel_difference(get_diff, diff, self._mb_entity_cache)
|
|
466
|
+
if updates:
|
|
467
|
+
self._log[__name__].debug('Got difference for channel %d updates', get_diff.channel.channel_id)
|
|
468
|
+
|
|
446
469
|
_preprocess_updates = await self._preprocess_updates(updates, users, chats)
|
|
447
470
|
updates_to_dispatch.extend(_preprocess_updates)
|
|
448
471
|
continue
|
|
@@ -475,6 +498,7 @@ class UpdateMethods:
|
|
|
475
498
|
try:
|
|
476
499
|
updates = await asyncio.wait_for(self._updates_queue.get(), deadline_delay)
|
|
477
500
|
except asyncio.TimeoutError:
|
|
501
|
+
self._log[__name__].debug('Timeout waiting for updates expired')
|
|
478
502
|
continue
|
|
479
503
|
else:
|
|
480
504
|
continue
|
|
@@ -625,7 +649,6 @@ class UpdateMethods:
|
|
|
625
649
|
if conv._custom:
|
|
626
650
|
await conv._check_custom(built)
|
|
627
651
|
|
|
628
|
-
tasks = []
|
|
629
652
|
for builder, callback in self._event_builders:
|
|
630
653
|
event = built[type(builder)]
|
|
631
654
|
if not event:
|
|
@@ -640,27 +663,32 @@ class UpdateMethods:
|
|
|
640
663
|
if not filter:
|
|
641
664
|
continue
|
|
642
665
|
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
666
|
+
try:
|
|
667
|
+
await callback(event)
|
|
668
|
+
except errors.AlreadyInConversationError:
|
|
669
|
+
name = getattr(callback, '__name__', repr(callback))
|
|
670
|
+
self._log[__name__].debug(
|
|
671
|
+
'Event handler "%s" already has an open conversation, '
|
|
672
|
+
'ignoring new one', name)
|
|
673
|
+
except events.StopPropagation:
|
|
674
|
+
name = getattr(callback, '__name__', repr(callback))
|
|
675
|
+
self._log[__name__].debug(
|
|
676
|
+
'Event handler "%s" stopped chain of propagation '
|
|
677
|
+
'for event %s.', name, type(event).__name__
|
|
678
|
+
)
|
|
679
|
+
break
|
|
680
|
+
except Exception as e:
|
|
681
|
+
if not isinstance(e, asyncio.CancelledError) or self.is_connected():
|
|
682
|
+
name = getattr(callback, '__name__', repr(callback))
|
|
683
|
+
self._log[__name__].exception('Unhandled exception on %s', name)
|
|
658
684
|
|
|
659
685
|
async def _dispatch_event(self: 'SoroushClient', event):
|
|
660
686
|
"""
|
|
661
687
|
Dispatches a single, out-of-order event. Used by `AlbumHack`.
|
|
662
688
|
"""
|
|
663
|
-
|
|
689
|
+
# We're duplicating a most logic from `_dispatch_update`, but all in
|
|
690
|
+
# the name of speed; we don't want to make it worse for all updates
|
|
691
|
+
# just because albums may need it.
|
|
664
692
|
for builder, callback in self._event_builders:
|
|
665
693
|
if isinstance(builder, events.Raw):
|
|
666
694
|
continue
|
|
@@ -676,21 +704,24 @@ class UpdateMethods:
|
|
|
676
704
|
if not filter:
|
|
677
705
|
continue
|
|
678
706
|
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
707
|
+
try:
|
|
708
|
+
await callback(event)
|
|
709
|
+
except errors.AlreadyInConversationError:
|
|
710
|
+
name = getattr(callback, '__name__', repr(callback))
|
|
711
|
+
self._log[__name__].debug(
|
|
712
|
+
'Event handler "%s" already has an open conversation, '
|
|
713
|
+
'ignoring new one', name)
|
|
714
|
+
except events.StopPropagation:
|
|
715
|
+
name = getattr(callback, '__name__', repr(callback))
|
|
716
|
+
self._log[__name__].debug(
|
|
717
|
+
'Event handler "%s" stopped chain of propagation '
|
|
718
|
+
'for event %s.', name, type(event).__name__
|
|
719
|
+
)
|
|
720
|
+
break
|
|
721
|
+
except Exception as e:
|
|
722
|
+
if not isinstance(e, asyncio.CancelledError) or self.is_connected():
|
|
723
|
+
name = getattr(callback, '__name__', repr(callback))
|
|
724
|
+
self._log[__name__].exception('Unhandled exception on %s', name)
|
|
694
725
|
|
|
695
726
|
async def _handle_auto_reconnect(self: 'SoroushClient'):
|
|
696
727
|
# Use lightweight get_me to let SoroushPlus know we want updates
|
|
@@ -701,12 +732,15 @@ class UpdateMethods:
|
|
|
701
732
|
'after reconnect: %s: %s', type(e), e)
|
|
702
733
|
|
|
703
734
|
try:
|
|
735
|
+
self._log[__name__].debug(
|
|
736
|
+
'Asking for the current state after reconnect...')
|
|
704
737
|
|
|
705
738
|
# If a disconnection occurs, the old known state will be
|
|
706
739
|
# the latest one we were aware of, so we can catch up since
|
|
707
740
|
# the most recent state we were aware of.
|
|
708
741
|
await self.catch_up()
|
|
709
742
|
|
|
743
|
+
self._log[__name__].debug('Successfully fetched missed updates')
|
|
710
744
|
except errors.RPCError as e:
|
|
711
745
|
self._log[__name__].warning('Failed to get missed updates after '
|
|
712
746
|
'reconnect: %r', e)
|
|
@@ -699,6 +699,8 @@ class UploadMethods:
|
|
|
699
699
|
hash_md5 = hashlib.md5()
|
|
700
700
|
|
|
701
701
|
part_count = (file_size + part_size - 1) // part_size
|
|
702
|
+
self._log[__name__].debug('Uploading file of %d bytes in %d chunks of %d',
|
|
703
|
+
file_size, part_count, part_size)
|
|
702
704
|
|
|
703
705
|
pos = 0
|
|
704
706
|
for part_index in range(part_count):
|
|
@@ -740,6 +742,8 @@ class UploadMethods:
|
|
|
740
742
|
|
|
741
743
|
result = await self(request)
|
|
742
744
|
if result:
|
|
745
|
+
self._log[__name__].debug('Uploaded %d/%d',
|
|
746
|
+
part_index + 1, part_count)
|
|
743
747
|
if progress_callback:
|
|
744
748
|
await helpers._maybe_await(progress_callback(pos, file_size))
|
|
745
749
|
else:
|
|
@@ -54,6 +54,7 @@ class UserMethods:
|
|
|
54
54
|
if diff <= 3: # Flood waits below 3 seconds are "ignored"
|
|
55
55
|
self._flood_waited_requests.pop(r.CONSTRUCTOR_ID, None)
|
|
56
56
|
elif diff <= flood_sleep_threshold:
|
|
57
|
+
self._log[__name__].debug(*_fmt_flood(diff, r, early=True))
|
|
57
58
|
await asyncio.sleep(diff)
|
|
58
59
|
self._flood_waited_requests.pop(r.CONSTRUCTOR_ID, None)
|
|
59
60
|
else:
|
|
@@ -124,12 +125,14 @@ class UserMethods:
|
|
|
124
125
|
e.seconds = 1
|
|
125
126
|
|
|
126
127
|
if e.seconds <= self.flood_sleep_threshold:
|
|
128
|
+
self._log[__name__].debug(*_fmt_flood(e.seconds, request))
|
|
127
129
|
await asyncio.sleep(e.seconds)
|
|
128
130
|
else:
|
|
129
131
|
raise
|
|
130
132
|
except (errors.PhoneMigrateError, errors.NetworkMigrateError,
|
|
131
133
|
errors.UserMigrateError) as e:
|
|
132
134
|
last_error = e
|
|
135
|
+
self._log[__name__].debug('Phone migrated to %d', e.new_dc)
|
|
133
136
|
should_raise = isinstance(e, (
|
|
134
137
|
errors.PhoneMigrateError, errors.NetworkMigrateError
|
|
135
138
|
))
|
|
@@ -178,6 +181,7 @@ class UserMethods:
|
|
|
178
181
|
|
|
179
182
|
return utils.get_input_peer(me, allow_self=False) if input_peer else me
|
|
180
183
|
except errors.UnauthorizedError as e:
|
|
184
|
+
_log.debug('Not authorized to get self user: %s', e)
|
|
181
185
|
return None
|
|
182
186
|
|
|
183
187
|
@property
|
|
@@ -225,6 +229,7 @@ class UserMethods:
|
|
|
225
229
|
await self(functions.updates.GetStateRequest())
|
|
226
230
|
self._authorized = True
|
|
227
231
|
except errors.RPCError as e:
|
|
232
|
+
_log.debug('Authorization check failed: %s', e)
|
|
228
233
|
self._authorized = False
|
|
229
234
|
except Exception:
|
|
230
235
|
raise
|
|
@@ -311,7 +316,7 @@ class UserMethods:
|
|
|
311
316
|
try:
|
|
312
317
|
lists[helpers._entity_type(x)].append(x)
|
|
313
318
|
except TypeError as e:
|
|
314
|
-
|
|
319
|
+
_log.debug('Could not determine entity type for %s: %s', x, e)
|
|
315
320
|
|
|
316
321
|
users = lists[helpers._EntityType.USER]
|
|
317
322
|
chats = lists[helpers._EntityType.CHAT]
|
|
@@ -424,7 +429,7 @@ class UserMethods:
|
|
|
424
429
|
try:
|
|
425
430
|
return utils.get_input_peer(peer)
|
|
426
431
|
except TypeError as e:
|
|
427
|
-
|
|
432
|
+
_log.debug('Could not convert peer to input_peer: %s', e)
|
|
428
433
|
|
|
429
434
|
# Next in priority is having a peer (or its ID) cached in-memory
|
|
430
435
|
try:
|
|
@@ -432,7 +437,7 @@ class UserMethods:
|
|
|
432
437
|
if isinstance(peer, int) or peer.SUBCLASS_OF_ID == 0x2d45687:
|
|
433
438
|
return self._mb_entity_cache.get(utils.get_peer_id(peer, add_mark=False))._as_input_peer()
|
|
434
439
|
except AttributeError as e:
|
|
435
|
-
|
|
440
|
+
_log.debug('Could not get input entity from cache: %s', e)
|
|
436
441
|
|
|
437
442
|
# Then come known strings that take precedence
|
|
438
443
|
if peer in ('me', 'self'):
|
|
@@ -443,7 +448,7 @@ class UserMethods:
|
|
|
443
448
|
input_entity = await utils.maybe_async(self.session.get_input_entity(peer))
|
|
444
449
|
return input_entity
|
|
445
450
|
except ValueError as e:
|
|
446
|
-
|
|
451
|
+
_log.debug('Entity not found in session: %s', e)
|
|
447
452
|
|
|
448
453
|
# Only network left to try
|
|
449
454
|
if isinstance(peer, str):
|
|
@@ -475,7 +480,7 @@ class UserMethods:
|
|
|
475
480
|
types.InputChannel(peer.channel_id, access_hash=0)]))
|
|
476
481
|
return utils.get_input_peer(channels.chats[0])
|
|
477
482
|
except errors.ChannelInvalidError as e:
|
|
478
|
-
|
|
483
|
+
_log.debug('Channel invalid: %s', e)
|
|
479
484
|
|
|
480
485
|
raise ValueError(
|
|
481
486
|
'Could not find the input entity for {} ({}). Please read https://'
|
|
@@ -514,6 +519,7 @@ class UserMethods:
|
|
|
514
519
|
# 0x2d45687, 0xc91c90b6 == crc32(b'Peer') and b'InputPeer'
|
|
515
520
|
peer = await self.get_input_entity(peer)
|
|
516
521
|
except AttributeError as e:
|
|
522
|
+
_log.debug('Peer has no SUBCLASS_OF_ID: %s', e)
|
|
517
523
|
peer = await self.get_input_entity(peer)
|
|
518
524
|
|
|
519
525
|
if isinstance(peer, types.InputPeerSelf):
|
|
@@ -577,13 +583,13 @@ class UserMethods:
|
|
|
577
583
|
else:
|
|
578
584
|
return next(x for x in result.chats if x.id == pid)
|
|
579
585
|
except StopIteration as e:
|
|
580
|
-
|
|
586
|
+
_log.debug('Entity not found in resolve result: %s', e)
|
|
581
587
|
try:
|
|
582
588
|
# Nobody with this username, maybe it's an exact name/title
|
|
583
589
|
input_entity = await utils.maybe_async(self.session.get_input_entity(string))
|
|
584
590
|
return await self.get_entity(input_entity)
|
|
585
591
|
except ValueError as e:
|
|
586
|
-
|
|
592
|
+
_log.debug('Entity not found by name: %s', e)
|
|
587
593
|
|
|
588
594
|
raise ValueError(
|
|
589
595
|
'Cannot find any entity corresponding to "{}"'.format(string)
|
|
@@ -602,7 +608,7 @@ class UserMethods:
|
|
|
602
608
|
elif dialog.SUBCLASS_OF_ID == 0xc91c90b6: # crc32(b'InputPeer')
|
|
603
609
|
return types.InputDialogPeer(dialog)
|
|
604
610
|
except AttributeError as e:
|
|
605
|
-
|
|
611
|
+
_log.debug('Dialog has no SUBCLASS_OF_ID: %s', e)
|
|
606
612
|
|
|
607
613
|
return types.InputDialogPeer(await self.get_input_entity(dialog))
|
|
608
614
|
|
|
@@ -618,7 +624,7 @@ class UserMethods:
|
|
|
618
624
|
notify.peer = await self.get_input_entity(notify.peer)
|
|
619
625
|
return notify
|
|
620
626
|
except AttributeError as e:
|
|
621
|
-
|
|
627
|
+
_log.debug('Notify has no SUBCLASS_OF_ID: %s', e)
|
|
622
628
|
|
|
623
629
|
return types.InputNotifyPeer(await self.get_input_entity(notify))
|
|
624
630
|
|