spluspy 2.0.8__tar.gz → 2.0.9__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.0.8 → spluspy-2.0.9}/CHANGELOG.md +10 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/PKG-INFO +1 -1
- {spluspy-2.0.8 → spluspy-2.0.9}/pyproject.toml +1 -1
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/__version__.py +1 -1
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/client/dialogs.py +97 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/client/client.py +312 -19
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/utils/version_check.py +2 -1
- {spluspy-2.0.8 → spluspy-2.0.9}/.gitignore +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/CONTRIBUTING.md +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/Dockerfile +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/LICENSE +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/MANIFEST.in +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/README.md +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/README.rst +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/examples/01_hello_bot.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/examples/02_echo_bot.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/examples/03_command_bot.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/examples/04_private_only.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/examples/05_group_only.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/examples/06_regex_bot.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/examples/07_user_filter.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/examples/08_combined_filters.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/examples/09_inline_keyboard.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/examples/10_reply_keyboard.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/examples/11_message_actions.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/examples/12_media_bot.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/examples/13_fsm_bot.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/examples/14_plugin_bot.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/examples/15_middleware_bot.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/examples/16_scheduler_bot.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/examples/17_conversation.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/examples/18_chat_management.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/examples/19_event_types.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/examples/20_sync_bot.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/examples/21_album_handler.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/examples/22_download_bot.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/examples/23_url_button.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/examples/24_string_session.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/examples/25_error_handling.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/examples/25_working_bot.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/examples/26_message_entities.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/examples/27_search_messages.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/examples/28_poll_bot.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/examples/29_dice_bot.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/examples/30_cache_bot.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/examples/31_reaction_bot.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/examples/32_mark_read_bot.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/examples/33_context_manager.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/examples/34_user_info.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/examples/35_chat_info.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/examples/36_forward_copy.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/examples/37_admin_bot.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/examples/38_media_filters.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/examples/39_status_bot.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/examples/40_delete_detection.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/examples/41_read_detection.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/examples/42_raw_events.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/examples/43_poll_handler.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/examples/44_reaction_handler.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/examples/45_typing_handler.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/examples/46_get_messages.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/examples/47_iter_messages.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/examples/48_get_members.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/examples/49_resolve_username.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/examples/50_invoke.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/setup.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/__init__.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/__init__.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/_updates/__init__.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/_updates/entitycache.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/_updates/messagebox.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/_updates/session.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/client/__init__.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/client/account.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/client/auth.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/client/bots.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/client/buttons.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/client/chats.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/client/downloads.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/client/messageparse.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/client/messages.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/client/soroushclient.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/client/telegrambaseclient.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/client/updates.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/client/uploads.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/client/users.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/crypto/__init__.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/crypto/aes.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/crypto/aesctr.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/crypto/authkey.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/crypto/cdndecrypter.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/crypto/factorization.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/crypto/fast_aes.pyx +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/crypto/fast_rsa.pyx +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/crypto/libssl.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/crypto/rsa.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/custom.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/errors/__init__.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/errors/common.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/errors/rpcbaseerrors.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/errors/rpcerrorlist.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/events/__init__.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/events/album.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/events/callbackquery.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/events/chataction.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/events/common.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/events/inlinequery.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/events/messagedeleted.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/events/messageedited.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/events/messageread.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/events/newmessage.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/events/raw.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/events/userupdate.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/extensions/__init__.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/extensions/binaryreader.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/extensions/html.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/extensions/markdown.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/extensions/messagepacker.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/functions.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/helpers.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/hints.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/network/__init__.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/network/authenticator.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/network/connection/__init__.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/network/connection/connection.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/network/connection/http.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/network/connection/tcpabridged.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/network/connection/tcpfull.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/network/connection/tcpintermediate.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/network/connection/tcpmtproxy.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/network/connection/tcpobfuscated.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/network/connection/websocket.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/network/mtprotoplainsender.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/network/mtprotosender.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/network/mtprotostate.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/network/requeststate.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/password.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/requestiter.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/sessions/__init__.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/sessions/abstract.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/sessions/memory.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/sessions/sqlite.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/sessions/string.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/sync.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/__init__.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/alltlobjects.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/core/__init__.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/core/gzippacked.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/core/messagecontainer.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/core/rpcresult.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/core/tlmessage.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/custom/__init__.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/custom/adminlogevent.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/custom/button.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/custom/chatgetter.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/custom/conversation.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/custom/dialog.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/custom/draft.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/custom/file.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/custom/forward.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/custom/inlinebuilder.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/custom/inlineresult.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/custom/inlineresults.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/custom/inputsizedfile.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/custom/message.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/custom/messagebutton.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/custom/participantpermissions.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/custom/qrlogin.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/custom/sendergetter.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/custom/types.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/functions/__init__.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/functions/account.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/functions/auth.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/functions/bots.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/functions/channels.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/functions/chatlists.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/functions/conference.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/functions/contacts.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/functions/folders.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/functions/help.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/functions/langpack.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/functions/messages.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/functions/payments.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/functions/phone.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/functions/photos.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/functions/premium.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/functions/stats.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/functions/stories.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/functions/thirdParty.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/functions/updates.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/functions/upload.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/functions/users.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/patched/__init__.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/tlobject.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/types/__init__.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/types/account.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/types/auth.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/types/bots.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/types/channels.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/types/chatlists.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/types/conference.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/types/contacts.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/types/help.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/types/messages.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/types/payments.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/types/phone.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/types/photos.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/types/premium.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/types/stats.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/types/stickers.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/types/storage.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/types/stories.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/types/thirdParty.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/types/update.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/types/updates.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/types/upload.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/tl/types/users.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/types.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/utils.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/_engine/version.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/admin.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/afk.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/cli.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/client/__init__.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/client/conversation.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/compat.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/config.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/errors/__init__.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/errors/exceptions.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/events/__init__.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/events/album.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/events/base.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/events/callback.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/events/chat_action.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/events/edited.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/events/inline.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/events/message.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/events/message_deleted.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/events/message_read.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/events/user_update.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/filters/__init__.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/filters/filters.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/fsm/__init__.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/fsm/state.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/middleware/__init__.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/middleware/base.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/middleware/rate_limit.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/mirror.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/models/__init__.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/models/bot.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/models/chat.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/models/enums.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/models/media.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/models/message.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/models/objects.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/models/user.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/network/__init__.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/network/client.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/network/connection.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/network/http_client.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/network/mtproto.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/network/protocols.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/network/spluspy_adapter.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/network/transport.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/plugins/__init__.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/plugins/loader.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/py.typed +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/scheduler/__init__.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/scheduler/scheduler.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/session/__init__.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/session/base.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/session/memory.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/session/sqlite.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/session/string.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/storage/__init__.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/storage/base.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/storage/entity_cache.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/storage/memory.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/storage/postgres.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/storage/redis.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/storage/sqlite.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/sync/__init__.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/sync/client.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/utils/__init__.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/utils/batch.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/utils/cache.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/utils/chat_lock.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/utils/file_transfer.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/utils/helpers.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/utils/logger.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/utils/rate_limiter.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/spluspy/utils/resolver.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/tests/__init__.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/tests/test_client.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/tests/test_filters.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/tests/test_fsm.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/tests/test_fsm_enhanced.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/tests/test_middleware.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/tests/test_rate_limiter.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/tests/test_resolver.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/tests/test_scheduler.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/tests/test_session.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/tests/test_storage.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/tests/test_types.py +0 -0
- {spluspy-2.0.8 → spluspy-2.0.9}/tests/test_utils.py +0 -0
|
@@ -5,6 +5,16 @@ 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.0.9] - 2026-07-12
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- `leave_all()` method to leave all groups and channels at once
|
|
12
|
+
- `delete_all_private()` method to delete all private chat dialogs
|
|
13
|
+
- `/delete_all_private` bot command with confirmation flow
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
- `/leave_all_groups` bot command now uses `leave_all()` to include channels
|
|
17
|
+
|
|
8
18
|
## [2.0.8] - 2026-07-12
|
|
9
19
|
|
|
10
20
|
### Changed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: spluspy
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.9
|
|
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
|
|
@@ -477,6 +477,103 @@ class DialogMethods:
|
|
|
477
477
|
|
|
478
478
|
return result
|
|
479
479
|
|
|
480
|
+
async def delete_all_private(
|
|
481
|
+
self: 'SoroushClient',
|
|
482
|
+
*,
|
|
483
|
+
revoke: bool = False,
|
|
484
|
+
exclude_ids: typing.Optional[typing.List[int]] = None,
|
|
485
|
+
delay: float = 1.0,
|
|
486
|
+
on_progress: typing.Optional[typing.Callable] = None
|
|
487
|
+
) -> dict:
|
|
488
|
+
"""
|
|
489
|
+
Delete all private chat dialogs (user conversations).
|
|
490
|
+
|
|
491
|
+
Iterates through all dialogs, identifies private chats (users),
|
|
492
|
+
and deletes them. Useful for cleaning up bot spam or old conversations.
|
|
493
|
+
|
|
494
|
+
Arguments
|
|
495
|
+
revoke (`bool`, optional):
|
|
496
|
+
If ``True``, revoke (delete) messages for the other peer too.
|
|
497
|
+
Default is ``False``.
|
|
498
|
+
|
|
499
|
+
exclude_ids (list of `int`, optional):
|
|
500
|
+
List of user IDs to skip (e.g. your own account, important contacts).
|
|
501
|
+
|
|
502
|
+
delay (`float`, optional):
|
|
503
|
+
Delay in seconds between each delete to avoid flood waits.
|
|
504
|
+
Default is ``1.0``.
|
|
505
|
+
|
|
506
|
+
on_progress (callable, optional):
|
|
507
|
+
Callback called with ``(current, total, title)`` for progress updates.
|
|
508
|
+
|
|
509
|
+
Returns
|
|
510
|
+
Dict with 'success', 'failed', 'skipped' counts and details.
|
|
511
|
+
|
|
512
|
+
Example
|
|
513
|
+
.. code-block:: python
|
|
514
|
+
|
|
515
|
+
# Delete all private chats
|
|
516
|
+
result = await client.delete_all_private()
|
|
517
|
+
print(f"Deleted {result['success']} chats")
|
|
518
|
+
|
|
519
|
+
# Delete but keep certain users
|
|
520
|
+
result = await client.delete_all_private(exclude_ids=[12345, 67890])
|
|
521
|
+
"""
|
|
522
|
+
exclude_ids = set(exclude_ids or [])
|
|
523
|
+
result = {"success": 0, "failed": 0, "skipped": 0, "details": []}
|
|
524
|
+
|
|
525
|
+
try:
|
|
526
|
+
dialogs = await self.get_dialogs(limit=None)
|
|
527
|
+
except Exception as exc:
|
|
528
|
+
_log.error("delete_all_private: failed to get dialogs: %s", exc)
|
|
529
|
+
return result
|
|
530
|
+
|
|
531
|
+
private_dialogs = []
|
|
532
|
+
for dialog in dialogs:
|
|
533
|
+
entity = dialog.entity
|
|
534
|
+
if entity is None:
|
|
535
|
+
continue
|
|
536
|
+
# Only user dialogs (private chats)
|
|
537
|
+
if not isinstance(entity, types.User):
|
|
538
|
+
continue
|
|
539
|
+
# Skip self
|
|
540
|
+
if entity.self:
|
|
541
|
+
continue
|
|
542
|
+
user_id = entity.id
|
|
543
|
+
if user_id in exclude_ids:
|
|
544
|
+
result["skipped"] += 1
|
|
545
|
+
result["details"].append({
|
|
546
|
+
"id": user_id,
|
|
547
|
+
"title": getattr(entity, 'first_name', None) or getattr(entity, 'username', str(user_id)),
|
|
548
|
+
"status": "skipped"
|
|
549
|
+
})
|
|
550
|
+
continue
|
|
551
|
+
private_dialogs.append((user_id, entity))
|
|
552
|
+
|
|
553
|
+
total = len(private_dialogs)
|
|
554
|
+
_log.info("delete_all_private: found %d private chats to delete", total)
|
|
555
|
+
|
|
556
|
+
for idx, (user_id, entity) in enumerate(private_dialogs):
|
|
557
|
+
title = getattr(entity, 'first_name', None) or getattr(entity, 'username', str(user_id))
|
|
558
|
+
if on_progress:
|
|
559
|
+
try:
|
|
560
|
+
on_progress(idx + 1, total, title)
|
|
561
|
+
except Exception:
|
|
562
|
+
pass
|
|
563
|
+
try:
|
|
564
|
+
await self.delete_dialog(entity, revoke=revoke)
|
|
565
|
+
result["success"] += 1
|
|
566
|
+
result["details"].append({"id": user_id, "title": title, "status": "deleted"})
|
|
567
|
+
_log.debug("delete_all_private: deleted %s (%d)", title, user_id)
|
|
568
|
+
except Exception as exc:
|
|
569
|
+
result["failed"] += 1
|
|
570
|
+
result["details"].append({"id": user_id, "title": title, "status": "failed"})
|
|
571
|
+
_log.warning("delete_all_private: failed to delete %s (%d): %s", title, user_id, exc)
|
|
572
|
+
if delay > 0 and idx < total - 1:
|
|
573
|
+
await asyncio.sleep(delay)
|
|
574
|
+
|
|
575
|
+
return result
|
|
576
|
+
|
|
480
577
|
def conversation(
|
|
481
578
|
self: 'SoroushClient',
|
|
482
579
|
entity: 'hints.EntityLike',
|
|
@@ -1786,6 +1786,9 @@ class Client:
|
|
|
1786
1786
|
async def leave_chat(self, chat_id: Union[int, str]) -> bool:
|
|
1787
1787
|
"""Leave a chat by ID, username, or invite link.
|
|
1788
1788
|
|
|
1789
|
+
Auto-detects whether the target is a channel/supergroup or a basic
|
|
1790
|
+
group and uses the correct TL method.
|
|
1791
|
+
|
|
1789
1792
|
Args:
|
|
1790
1793
|
chat_id: Chat integer ID, username (str), or t.me link.
|
|
1791
1794
|
|
|
@@ -1794,22 +1797,9 @@ class Client:
|
|
|
1794
1797
|
"""
|
|
1795
1798
|
client = self._get_client()
|
|
1796
1799
|
if not client:
|
|
1797
|
-
logger.warning("leave_chat skipped: client not connected")
|
|
1798
1800
|
return False
|
|
1799
1801
|
resolved = self._resolve_chat_id(chat_id)
|
|
1800
|
-
try:
|
|
1801
|
-
if hasattr(client, "leave_chat"):
|
|
1802
|
-
await client.leave_chat(resolved)
|
|
1803
|
-
else:
|
|
1804
|
-
await self._leave_chat_via_invoke(client, resolved)
|
|
1805
|
-
logger.debug("leave_chat(%s) succeeded", chat_id)
|
|
1806
|
-
return True
|
|
1807
|
-
except Exception as exc:
|
|
1808
|
-
logger.error("leave_chat(%s) failed: %s", chat_id, exc)
|
|
1809
|
-
return False
|
|
1810
1802
|
|
|
1811
|
-
async def _leave_chat_via_invoke(self, client: Any, resolved: Any) -> None:
|
|
1812
|
-
"""Leave a chat using raw TL invoke as a fallback."""
|
|
1813
1803
|
try:
|
|
1814
1804
|
from spluspy._engine import functions, types # type: ignore[import-untyped]
|
|
1815
1805
|
except ImportError:
|
|
@@ -1818,14 +1808,317 @@ class Client:
|
|
|
1818
1808
|
except ImportError:
|
|
1819
1809
|
raise ImportError("No compatible MTProto library available for leave_chat")
|
|
1820
1810
|
|
|
1821
|
-
|
|
1811
|
+
# 1. Try get_entity to detect type
|
|
1812
|
+
try:
|
|
1813
|
+
entity = await client.get_entity(resolved)
|
|
1814
|
+
if isinstance(entity, types.Channel):
|
|
1815
|
+
return await self.leave_channel(chat_id)
|
|
1816
|
+
elif isinstance(entity, types.Chat):
|
|
1817
|
+
return await self.leave_group(chat_id)
|
|
1818
|
+
else:
|
|
1819
|
+
return await self.leave_channel(chat_id)
|
|
1820
|
+
except Exception:
|
|
1821
|
+
pass
|
|
1822
1822
|
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1823
|
+
# 2. Fallback: try get_input_entity
|
|
1824
|
+
try:
|
|
1825
|
+
input_peer = await client.get_input_entity(resolved)
|
|
1826
|
+
if isinstance(input_peer, types.InputPeerChannel):
|
|
1827
|
+
return await self.leave_channel(chat_id)
|
|
1828
|
+
elif isinstance(input_peer, types.InputPeerChat):
|
|
1829
|
+
return await self.leave_group(chat_id)
|
|
1830
|
+
else:
|
|
1831
|
+
return await self.leave_channel(chat_id)
|
|
1832
|
+
except Exception:
|
|
1833
|
+
pass
|
|
1834
|
+
|
|
1835
|
+
# 3. Fallback: try client.leave_chat
|
|
1836
|
+
if hasattr(client, "leave_chat"):
|
|
1837
|
+
try:
|
|
1838
|
+
await client.leave_chat(resolved)
|
|
1839
|
+
return True
|
|
1840
|
+
except Exception:
|
|
1841
|
+
pass
|
|
1842
|
+
|
|
1843
|
+
return False
|
|
1844
|
+
|
|
1845
|
+
async def leave_group(self, chat_id: Union[int, str]) -> bool:
|
|
1846
|
+
"""Leave a basic group (supergroup or regular group).
|
|
1847
|
+
|
|
1848
|
+
Uses DeleteChatUserRequest — the correct method for groups.
|
|
1849
|
+
|
|
1850
|
+
Args:
|
|
1851
|
+
chat_id: Group integer ID.
|
|
1852
|
+
|
|
1853
|
+
Returns:
|
|
1854
|
+
True on success, False on failure.
|
|
1855
|
+
"""
|
|
1856
|
+
client = self._get_client()
|
|
1857
|
+
if not client:
|
|
1858
|
+
return False
|
|
1859
|
+
resolved = self._resolve_chat_id(chat_id)
|
|
1860
|
+
|
|
1861
|
+
try:
|
|
1862
|
+
from spluspy._engine import functions # type: ignore[import-untyped]
|
|
1863
|
+
except ImportError:
|
|
1864
|
+
try:
|
|
1865
|
+
from telethon import functions # type: ignore[import-untyped]
|
|
1866
|
+
except ImportError:
|
|
1867
|
+
raise ImportError("No compatible MTProto library available")
|
|
1868
|
+
|
|
1869
|
+
# Strategy 1: kick_participant('me')
|
|
1870
|
+
if hasattr(client, 'kick_participant'):
|
|
1871
|
+
try:
|
|
1872
|
+
await client.kick_participant(resolved, 'me')
|
|
1873
|
+
return True
|
|
1874
|
+
except Exception:
|
|
1875
|
+
pass
|
|
1876
|
+
|
|
1877
|
+
# Strategy 2: get_entity → DeleteChatUserRequest
|
|
1878
|
+
try:
|
|
1879
|
+
entity = await client.get_entity(resolved)
|
|
1880
|
+
chat_id_raw = getattr(entity, 'chat_id', None) or getattr(entity, 'id', None)
|
|
1881
|
+
await client(functions.messages.DeleteChatUserRequest(
|
|
1882
|
+
chat_id=chat_id_raw, user_id="me"
|
|
1883
|
+
))
|
|
1884
|
+
return True
|
|
1885
|
+
except Exception:
|
|
1886
|
+
pass
|
|
1887
|
+
|
|
1888
|
+
# Strategy 3: get_input_entity → DeleteChatUserRequest
|
|
1889
|
+
try:
|
|
1890
|
+
input_peer = await client.get_input_entity(resolved)
|
|
1891
|
+
chat_id_raw = getattr(input_peer, 'chat_id', None)
|
|
1892
|
+
if chat_id_raw is not None:
|
|
1893
|
+
await client(functions.messages.DeleteChatUserRequest(
|
|
1894
|
+
chat_id=chat_id_raw, user_id="me"
|
|
1895
|
+
))
|
|
1896
|
+
return True
|
|
1897
|
+
except Exception:
|
|
1898
|
+
pass
|
|
1899
|
+
|
|
1900
|
+
return False
|
|
1901
|
+
|
|
1902
|
+
async def leave_channel(self, chat_id: Union[int, str]) -> bool:
|
|
1903
|
+
"""Leave a channel or supergroup.
|
|
1904
|
+
|
|
1905
|
+
Uses LeaveChannelRequest — the correct method for channels.
|
|
1906
|
+
|
|
1907
|
+
Args:
|
|
1908
|
+
chat_id: Channel integer ID or username.
|
|
1909
|
+
|
|
1910
|
+
Returns:
|
|
1911
|
+
True on success, False on failure.
|
|
1912
|
+
"""
|
|
1913
|
+
client = self._get_client()
|
|
1914
|
+
if not client:
|
|
1915
|
+
return False
|
|
1916
|
+
resolved = self._resolve_chat_id(chat_id)
|
|
1917
|
+
|
|
1918
|
+
try:
|
|
1919
|
+
from spluspy._engine import functions # type: ignore[import-untyped]
|
|
1920
|
+
except ImportError:
|
|
1921
|
+
try:
|
|
1922
|
+
from telethon import functions # type: ignore[import-untyped]
|
|
1923
|
+
except ImportError:
|
|
1924
|
+
raise ImportError("No compatible MTProto library available")
|
|
1925
|
+
|
|
1926
|
+
# Strategy 1: get_entity → LeaveChannelRequest
|
|
1927
|
+
try:
|
|
1928
|
+
entity = await client.get_entity(resolved)
|
|
1828
1929
|
await client(functions.channels.LeaveChannelRequest(channel=entity))
|
|
1930
|
+
return True
|
|
1931
|
+
except Exception:
|
|
1932
|
+
pass
|
|
1933
|
+
|
|
1934
|
+
# Strategy 2: get_input_entity → LeaveChannelRequest
|
|
1935
|
+
try:
|
|
1936
|
+
input_peer = await client.get_input_entity(resolved)
|
|
1937
|
+
await client(functions.channels.LeaveChannelRequest(channel=input_peer))
|
|
1938
|
+
return True
|
|
1939
|
+
except Exception:
|
|
1940
|
+
pass
|
|
1941
|
+
|
|
1942
|
+
# Strategy 3: client.leave_chat fallback
|
|
1943
|
+
if hasattr(client, "leave_chat"):
|
|
1944
|
+
try:
|
|
1945
|
+
await client.leave_chat(resolved)
|
|
1946
|
+
return True
|
|
1947
|
+
except Exception:
|
|
1948
|
+
pass
|
|
1949
|
+
|
|
1950
|
+
return False
|
|
1951
|
+
|
|
1952
|
+
async def get_all_groups(self) -> list[dict]:
|
|
1953
|
+
"""Get all groups and channels the bot is a member of.
|
|
1954
|
+
|
|
1955
|
+
Returns:
|
|
1956
|
+
List of dicts with keys: id, title, type ('channel' or 'chat')
|
|
1957
|
+
"""
|
|
1958
|
+
client = self._get_client()
|
|
1959
|
+
if not client:
|
|
1960
|
+
logger.warning("get_all_groups skipped: client not connected")
|
|
1961
|
+
return []
|
|
1962
|
+
|
|
1963
|
+
try:
|
|
1964
|
+
dialogs = await client.get_dialogs(limit=None)
|
|
1965
|
+
except Exception as exc:
|
|
1966
|
+
logger.error("get_all_groups: failed to get dialogs: %s", exc)
|
|
1967
|
+
return []
|
|
1968
|
+
|
|
1969
|
+
groups = []
|
|
1970
|
+
for dialog in dialogs:
|
|
1971
|
+
entity = dialog.entity
|
|
1972
|
+
if entity is None:
|
|
1973
|
+
continue
|
|
1974
|
+
entity_type = type(entity).__name__
|
|
1975
|
+
# Skip users (not groups/channels)
|
|
1976
|
+
if "User" in entity_type:
|
|
1977
|
+
continue
|
|
1978
|
+
# Skip self
|
|
1979
|
+
if getattr(entity, 'id', None) == self._user_id:
|
|
1980
|
+
continue
|
|
1981
|
+
groups.append({
|
|
1982
|
+
"id": getattr(entity, 'id', 0),
|
|
1983
|
+
"title": getattr(entity, 'title', 'Unknown'),
|
|
1984
|
+
"type": "channel" if "Channel" in entity_type else "chat",
|
|
1985
|
+
"entity": entity,
|
|
1986
|
+
})
|
|
1987
|
+
|
|
1988
|
+
logger.debug("get_all_groups: found %d groups/channels", len(groups))
|
|
1989
|
+
return groups
|
|
1990
|
+
|
|
1991
|
+
async def leave_all_groups(self, exclude_ids: list[int] = None) -> dict:
|
|
1992
|
+
"""Leave all basic groups and supergroups (not channels).
|
|
1993
|
+
|
|
1994
|
+
Args:
|
|
1995
|
+
exclude_ids: List of chat IDs to skip.
|
|
1996
|
+
|
|
1997
|
+
Returns:
|
|
1998
|
+
Dict with 'success', 'failed', 'skipped' counts and details.
|
|
1999
|
+
"""
|
|
2000
|
+
exclude_ids = set(exclude_ids or [])
|
|
2001
|
+
all_groups = await self.get_all_groups()
|
|
2002
|
+
result = {"success": 0, "failed": 0, "skipped": 0, "details": []}
|
|
2003
|
+
|
|
2004
|
+
for g in all_groups:
|
|
2005
|
+
if g["type"] == "channel":
|
|
2006
|
+
continue # only groups
|
|
2007
|
+
chat_id = g["id"]
|
|
2008
|
+
if chat_id in exclude_ids:
|
|
2009
|
+
result["skipped"] += 1
|
|
2010
|
+
result["details"].append({"id": chat_id, "title": g["title"], "status": "skipped"})
|
|
2011
|
+
continue
|
|
2012
|
+
|
|
2013
|
+
ok = await self.leave_group(chat_id)
|
|
2014
|
+
if ok:
|
|
2015
|
+
result["success"] += 1
|
|
2016
|
+
result["details"].append({"id": chat_id, "title": g["title"], "status": "left"})
|
|
2017
|
+
else:
|
|
2018
|
+
result["failed"] += 1
|
|
2019
|
+
result["details"].append({"id": chat_id, "title": g["title"], "status": "failed"})
|
|
2020
|
+
await asyncio.sleep(1)
|
|
2021
|
+
|
|
2022
|
+
return result
|
|
2023
|
+
|
|
2024
|
+
async def leave_all_channels(self, exclude_ids: list[int] = None) -> dict:
|
|
2025
|
+
"""Leave all channels (not groups).
|
|
2026
|
+
|
|
2027
|
+
Args:
|
|
2028
|
+
exclude_ids: List of chat IDs to skip.
|
|
2029
|
+
|
|
2030
|
+
Returns:
|
|
2031
|
+
Dict with 'success', 'failed', 'skipped' counts and details.
|
|
2032
|
+
"""
|
|
2033
|
+
exclude_ids = set(exclude_ids or [])
|
|
2034
|
+
all_groups = await self.get_all_groups()
|
|
2035
|
+
result = {"success": 0, "failed": 0, "skipped": 0, "details": []}
|
|
2036
|
+
|
|
2037
|
+
for g in all_groups:
|
|
2038
|
+
if g["type"] == "chat":
|
|
2039
|
+
continue # only channels
|
|
2040
|
+
chat_id = g["id"]
|
|
2041
|
+
if chat_id in exclude_ids:
|
|
2042
|
+
result["skipped"] += 1
|
|
2043
|
+
result["details"].append({"id": chat_id, "title": g["title"], "status": "skipped"})
|
|
2044
|
+
continue
|
|
2045
|
+
|
|
2046
|
+
ok = await self.leave_channel(chat_id)
|
|
2047
|
+
if ok:
|
|
2048
|
+
result["success"] += 1
|
|
2049
|
+
result["details"].append({"id": chat_id, "title": g["title"], "status": "left"})
|
|
2050
|
+
else:
|
|
2051
|
+
result["failed"] += 1
|
|
2052
|
+
result["details"].append({"id": chat_id, "title": g["title"], "status": "failed"})
|
|
2053
|
+
await asyncio.sleep(1)
|
|
2054
|
+
|
|
2055
|
+
return result
|
|
2056
|
+
|
|
2057
|
+
async def leave_all(self, exclude_ids: list[int] = None) -> dict:
|
|
2058
|
+
"""Leave all groups and channels.
|
|
2059
|
+
|
|
2060
|
+
Args:
|
|
2061
|
+
exclude_ids: List of chat IDs to skip.
|
|
2062
|
+
|
|
2063
|
+
Returns:
|
|
2064
|
+
Dict with 'success', 'failed', 'skipped' counts and details.
|
|
2065
|
+
"""
|
|
2066
|
+
exclude_ids = set(exclude_ids or [])
|
|
2067
|
+
all_groups = await self.get_all_groups()
|
|
2068
|
+
result = {"success": 0, "failed": 0, "skipped": 0, "details": []}
|
|
2069
|
+
|
|
2070
|
+
for g in all_groups:
|
|
2071
|
+
chat_id = g["id"]
|
|
2072
|
+
if chat_id in exclude_ids:
|
|
2073
|
+
result["skipped"] += 1
|
|
2074
|
+
result["details"].append({"id": chat_id, "title": g["title"], "status": "skipped"})
|
|
2075
|
+
continue
|
|
2076
|
+
|
|
2077
|
+
if g["type"] == "channel":
|
|
2078
|
+
ok = await self.leave_channel(chat_id)
|
|
2079
|
+
else:
|
|
2080
|
+
ok = await self.leave_group(chat_id)
|
|
2081
|
+
|
|
2082
|
+
if ok:
|
|
2083
|
+
result["success"] += 1
|
|
2084
|
+
result["details"].append({"id": chat_id, "title": g["title"], "status": "left"})
|
|
2085
|
+
else:
|
|
2086
|
+
result["failed"] += 1
|
|
2087
|
+
result["details"].append({"id": chat_id, "title": g["title"], "status": "failed"})
|
|
2088
|
+
await asyncio.sleep(1)
|
|
2089
|
+
|
|
2090
|
+
return result
|
|
2091
|
+
|
|
2092
|
+
async def delete_all_private(
|
|
2093
|
+
self,
|
|
2094
|
+
*,
|
|
2095
|
+
revoke: bool = False,
|
|
2096
|
+
exclude_ids: list[int] = None,
|
|
2097
|
+
delay: float = 1.0,
|
|
2098
|
+
on_progress=None,
|
|
2099
|
+
) -> dict:
|
|
2100
|
+
"""Delete all private chat dialogs (user conversations).
|
|
2101
|
+
|
|
2102
|
+
Args:
|
|
2103
|
+
revoke: If True, revoke messages for the other peer too.
|
|
2104
|
+
exclude_ids: List of user IDs to skip.
|
|
2105
|
+
delay: Delay in seconds between each delete.
|
|
2106
|
+
on_progress: Callback ``(current, total, title)`` for progress.
|
|
2107
|
+
|
|
2108
|
+
Returns:
|
|
2109
|
+
Dict with 'success', 'failed', 'skipped' counts and details.
|
|
2110
|
+
"""
|
|
2111
|
+
client = self._get_client()
|
|
2112
|
+
if not client:
|
|
2113
|
+
logger.warning("delete_all_private skipped: client not connected")
|
|
2114
|
+
return {"success": 0, "failed": 0, "skipped": 0, "details": []}
|
|
2115
|
+
|
|
2116
|
+
return await client.delete_all_private(
|
|
2117
|
+
revoke=revoke,
|
|
2118
|
+
exclude_ids=exclude_ids,
|
|
2119
|
+
delay=delay,
|
|
2120
|
+
on_progress=on_progress,
|
|
2121
|
+
)
|
|
1829
2122
|
|
|
1830
2123
|
async def pin_message(self, chat_id: int, message: Union[Message, int], *, notify: bool = False) -> bool:
|
|
1831
2124
|
"""Pin a message in a chat.
|
|
@@ -70,7 +70,8 @@ def print_update_notice(new_version: str) -> None:
|
|
|
70
70
|
print(
|
|
71
71
|
f"\n\033[33m⚠ SplusPy {new_version} is available "
|
|
72
72
|
f"(installed: {__version__}).\033[0m\n"
|
|
73
|
-
f"\033[33m Run: pip install --upgrade spluspy\033[0m\n"
|
|
73
|
+
f"\033[33m Run: pip install --upgrade spluspy\033[0m\n"
|
|
74
|
+
f"\033[33m Channel » @SplusPy\033[0m\n",
|
|
74
75
|
file=sys.stderr,
|
|
75
76
|
)
|
|
76
77
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|