spluspy 2.1.0__tar.gz → 2.1.2__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.0 → spluspy-2.1.2}/CHANGELOG.md +5 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/PKG-INFO +1 -1
- {spluspy-2.1.0 → spluspy-2.1.2}/pyproject.toml +1 -1
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/__version__.py +1 -1
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/version.py +1 -1
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/client/chat_mixin.py +466 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/client/client.py +143 -1
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/config.py +2 -2
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/sync/client.py +3 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/.gitignore +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/CONTRIBUTING.md +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/Dockerfile +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/LICENSE +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/MANIFEST.in +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/README.md +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/README.rst +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/examples/01_hello_bot.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/examples/02_echo_bot.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/examples/03_command_bot.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/examples/04_private_only.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/examples/05_group_only.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/examples/06_regex_bot.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/examples/07_user_filter.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/examples/08_combined_filters.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/examples/09_inline_keyboard.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/examples/10_reply_keyboard.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/examples/11_message_actions.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/examples/12_media_bot.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/examples/13_fsm_bot.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/examples/14_plugin_bot.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/examples/15_middleware_bot.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/examples/16_scheduler_bot.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/examples/17_conversation.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/examples/18_chat_management.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/examples/19_event_types.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/examples/20_sync_bot.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/examples/21_album_handler.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/examples/22_download_bot.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/examples/23_url_button.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/examples/24_string_session.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/examples/25_error_handling.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/examples/25_working_bot.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/examples/26_message_entities.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/examples/27_search_messages.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/examples/28_poll_bot.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/examples/29_dice_bot.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/examples/30_cache_bot.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/examples/31_reaction_bot.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/examples/32_mark_read_bot.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/examples/33_context_manager.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/examples/34_user_info.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/examples/35_chat_info.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/examples/36_forward_copy.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/examples/37_admin_bot.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/examples/38_media_filters.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/examples/39_status_bot.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/examples/40_delete_detection.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/examples/41_read_detection.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/examples/42_raw_events.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/examples/43_poll_handler.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/examples/44_reaction_handler.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/examples/45_typing_handler.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/examples/46_get_messages.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/examples/47_iter_messages.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/examples/48_get_members.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/examples/49_resolve_username.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/examples/50_invoke.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/setup.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/__init__.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/__init__.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/_updates/__init__.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/_updates/entitycache.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/_updates/messagebox.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/_updates/session.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/client/__init__.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/client/account.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/client/auth.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/client/bots.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/client/buttons.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/client/chats.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/client/dialogs.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/client/downloads.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/client/messageparse.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/client/messages.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/client/soroushclient.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/client/telegrambaseclient.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/client/updates.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/client/uploads.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/client/users.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/crypto/__init__.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/crypto/aes.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/crypto/aesctr.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/crypto/authkey.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/crypto/cdndecrypter.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/crypto/factorization.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/crypto/fast_aes.pyx +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/crypto/fast_rsa.pyx +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/crypto/libssl.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/crypto/rsa.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/custom.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/errors/__init__.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/errors/common.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/errors/rpcbaseerrors.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/errors/rpcerrorlist.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/events/__init__.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/events/album.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/events/callbackquery.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/events/chataction.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/events/common.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/events/inlinequery.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/events/messagedeleted.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/events/messageedited.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/events/messageread.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/events/newmessage.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/events/raw.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/events/userupdate.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/extensions/__init__.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/extensions/binaryreader.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/extensions/html.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/extensions/markdown.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/extensions/messagepacker.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/functions.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/helpers.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/hints.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/network/__init__.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/network/authenticator.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/network/connection/__init__.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/network/connection/connection.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/network/connection/http.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/network/connection/tcpabridged.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/network/connection/tcpfull.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/network/connection/tcpintermediate.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/network/connection/tcpmtproxy.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/network/connection/tcpobfuscated.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/network/connection/websocket.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/network/mtprotoplainsender.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/network/mtprotosender.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/network/mtprotostate.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/network/requeststate.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/password.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/requestiter.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/sessions/__init__.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/sessions/abstract.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/sessions/memory.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/sessions/sqlite.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/sessions/string.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/sync.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/__init__.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/alltlobjects.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/core/__init__.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/core/gzippacked.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/core/messagecontainer.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/core/rpcresult.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/core/tlmessage.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/custom/__init__.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/custom/adminlogevent.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/custom/button.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/custom/chatgetter.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/custom/conversation.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/custom/dialog.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/custom/draft.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/custom/file.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/custom/forward.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/custom/inlinebuilder.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/custom/inlineresult.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/custom/inlineresults.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/custom/inputsizedfile.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/custom/message.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/custom/messagebutton.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/custom/participantpermissions.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/custom/qrlogin.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/custom/sendergetter.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/custom/types.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/functions/__init__.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/functions/account.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/functions/auth.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/functions/bots.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/functions/channels.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/functions/chatlists.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/functions/conference.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/functions/contacts.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/functions/folders.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/functions/help.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/functions/langpack.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/functions/messages.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/functions/payments.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/functions/phone.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/functions/photos.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/functions/premium.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/functions/stats.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/functions/stories.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/functions/thirdParty.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/functions/updates.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/functions/upload.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/functions/users.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/patched/__init__.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/tlobject.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/types/__init__.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/types/account.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/types/auth.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/types/bots.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/types/channels.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/types/chatlists.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/types/conference.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/types/contacts.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/types/help.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/types/messages.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/types/payments.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/types/phone.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/types/photos.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/types/premium.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/types/stats.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/types/stickers.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/types/storage.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/types/stories.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/types/thirdParty.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/types/update.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/types/updates.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/types/upload.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/tl/types/users.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/types.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/_engine/utils.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/admin.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/afk.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/cli.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/client/__init__.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/client/conversation.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/client/media_mixin.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/compat.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/errors/__init__.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/errors/exceptions.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/events/__init__.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/events/album.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/events/base.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/events/callback.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/events/chat_action.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/events/edited.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/events/inline.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/events/message.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/events/message_deleted.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/events/message_read.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/events/user_update.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/filters/__init__.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/filters/filters.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/fsm/__init__.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/fsm/state.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/middleware/__init__.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/middleware/base.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/middleware/rate_limit.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/mirror.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/models/__init__.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/models/bot.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/models/chat.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/models/enums.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/models/media.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/models/message.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/models/objects.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/models/user.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/network/__init__.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/network/client.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/network/connection.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/network/http_client.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/network/mtproto.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/network/protocols.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/network/spluspy_adapter.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/network/transport.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/plugins/__init__.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/plugins/loader.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/py.typed +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/scheduler/__init__.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/scheduler/scheduler.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/session/__init__.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/session/base.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/session/memory.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/session/sqlite.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/session/string.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/storage/__init__.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/storage/base.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/storage/entity_cache.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/storage/memory.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/storage/postgres.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/storage/redis.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/storage/sqlite.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/sync/__init__.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/utils/__init__.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/utils/batch.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/utils/cache.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/utils/chat_lock.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/utils/file_transfer.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/utils/helpers.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/utils/logger.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/utils/rate_limiter.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/utils/resolver.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/spluspy/utils/version_check.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/tests/__init__.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/tests/test_client.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/tests/test_filters.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/tests/test_fsm.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/tests/test_fsm_enhanced.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/tests/test_middleware.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/tests/test_rate_limiter.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/tests/test_resolver.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/tests/test_scheduler.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/tests/test_session.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/tests/test_storage.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/tests/test_types.py +0 -0
- {spluspy-2.1.0 → spluspy-2.1.2}/tests/test_utils.py +0 -0
|
@@ -5,6 +5,11 @@ 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.1] - 2026-07-12
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
- Version bump to 2.1.1
|
|
12
|
+
|
|
8
13
|
## [2.1.0] - 2026-07-12
|
|
9
14
|
|
|
10
15
|
### Changed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: spluspy
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.2
|
|
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
|
|
@@ -398,6 +398,472 @@ class ChatManagementMixin:
|
|
|
398
398
|
)
|
|
399
399
|
return False
|
|
400
400
|
|
|
401
|
+
async def set_chat_permissions(
|
|
402
|
+
self,
|
|
403
|
+
chat_id: Union[int, str],
|
|
404
|
+
send_messages: Optional[bool] = None,
|
|
405
|
+
send_media: Optional[bool] = None,
|
|
406
|
+
send_stickers: Optional[bool] = None,
|
|
407
|
+
send_gifs: Optional[bool] = None,
|
|
408
|
+
send_polls: Optional[bool] = None,
|
|
409
|
+
embed_links: Optional[bool] = None,
|
|
410
|
+
invite_users: Optional[bool] = None,
|
|
411
|
+
change_info: Optional[bool] = None,
|
|
412
|
+
pin_messages: Optional[bool] = None,
|
|
413
|
+
) -> bool:
|
|
414
|
+
"""Set the default chat permissions (default banned rights) for a group.
|
|
415
|
+
|
|
416
|
+
Controls what actions regular members are allowed to perform.
|
|
417
|
+
This applies to ALL non-admin members of the group.
|
|
418
|
+
|
|
419
|
+
Args:
|
|
420
|
+
chat_id: Channel/supergroup identifier (int ID, @username, etc.).
|
|
421
|
+
send_messages: ``True`` to ALLOW sending text messages,
|
|
422
|
+
``False`` to RESTRICT. ``None`` keeps current state.
|
|
423
|
+
send_media: ``True`` to ALLOW sending media (photos, videos, etc.),
|
|
424
|
+
``False`` to RESTRICT. ``None`` keeps current state.
|
|
425
|
+
send_stickers: ``True`` to ALLOW sending stickers,
|
|
426
|
+
``False`` to RESTRICT. ``None`` keeps current state.
|
|
427
|
+
send_gifs: ``True`` to ALLOW sending GIFs,
|
|
428
|
+
``False`` to RESTRICT. ``None`` keeps current state.
|
|
429
|
+
send_polls: ``True`` to ALLOW sending polls,
|
|
430
|
+
``False`` to RESTRICT. ``None`` keeps current state.
|
|
431
|
+
embed_links: ``True`` to ALLOW embedding links in messages,
|
|
432
|
+
``False`` to RESTRICT. ``None`` keeps current state.
|
|
433
|
+
invite_users: ``True`` to ALLOW inviting other users,
|
|
434
|
+
``False`` to RESTRICT. ``None`` keeps current state.
|
|
435
|
+
change_info: ``True`` to ALLOW changing group info,
|
|
436
|
+
``False`` to RESTRICT. ``None`` keeps current state.
|
|
437
|
+
pin_messages: ``True`` to ALLOW pinning messages,
|
|
438
|
+
``False`` to RESTRICT. ``None`` keeps current state.
|
|
439
|
+
|
|
440
|
+
Returns:
|
|
441
|
+
``True`` on success, ``False`` on failure.
|
|
442
|
+
|
|
443
|
+
Note:
|
|
444
|
+
In MTProto, ``ChatBannedRights`` flags are **positive-ban**:
|
|
445
|
+
setting a flag to ``True`` means the user is BANNED from that
|
|
446
|
+
action. This method inverts the semantics so that ``True``
|
|
447
|
+
means "allowed" (intuitive for the developer).
|
|
448
|
+
|
|
449
|
+
Example:
|
|
450
|
+
>>> # Lock everything: no messages, no media, no stickers
|
|
451
|
+
>>> await client.set_chat_permissions(chat_id,
|
|
452
|
+
... send_messages=False, send_media=False,
|
|
453
|
+
... send_stickers=False)
|
|
454
|
+
>>>
|
|
455
|
+
>>> # Only restrict media; keep everything else as-is
|
|
456
|
+
>>> await client.set_chat_permissions(chat_id,
|
|
457
|
+
... send_media=False)
|
|
458
|
+
>>>
|
|
459
|
+
>>> # Unlock everything
|
|
460
|
+
>>> await client.set_chat_permissions(chat_id,
|
|
461
|
+
... send_messages=True, send_media=True,
|
|
462
|
+
... send_stickers=True, send_gifs=True,
|
|
463
|
+
... send_polls=True, embed_links=True,
|
|
464
|
+
... invite_users=True, change_info=True,
|
|
465
|
+
... pin_messages=True)
|
|
466
|
+
"""
|
|
467
|
+
client = self._get_client()
|
|
468
|
+
if not client:
|
|
469
|
+
logger.warning("set_chat_permissions skipped: client not connected")
|
|
470
|
+
return False
|
|
471
|
+
|
|
472
|
+
try:
|
|
473
|
+
from spluspy._engine import functions, types
|
|
474
|
+
except ImportError:
|
|
475
|
+
from telethon import functions, types
|
|
476
|
+
|
|
477
|
+
try:
|
|
478
|
+
# Resolve chat_id to InputPeer
|
|
479
|
+
input_peer = await self._resolve_to_input_peer(client, chat_id)
|
|
480
|
+
if input_peer is None:
|
|
481
|
+
# Fallback: try get_input_entity directly
|
|
482
|
+
try:
|
|
483
|
+
input_peer = await client.get_input_entity(chat_id)
|
|
484
|
+
except Exception:
|
|
485
|
+
logger.error(
|
|
486
|
+
"set_chat_permissions: cannot resolve %s to InputPeer",
|
|
487
|
+
chat_id,
|
|
488
|
+
)
|
|
489
|
+
return False
|
|
490
|
+
|
|
491
|
+
# Map developer-friendly booleans to MTProto banned-rights.
|
|
492
|
+
# In MTProto: True = BANNED (restricted).
|
|
493
|
+
# Here: True = ALLOWED (not banned), False = RESTRICTED (banned).
|
|
494
|
+
# None means "don't change" (omit from ChatBannedRights).
|
|
495
|
+
|
|
496
|
+
def _to_banned(val: Optional[bool]) -> Optional[bool]:
|
|
497
|
+
"""Convert developer-friendly bool to MTProto banned flag.
|
|
498
|
+
|
|
499
|
+
True (allowed) -> False (not banned)
|
|
500
|
+
False (restricted) -> True (banned)
|
|
501
|
+
None -> None (omit)
|
|
502
|
+
"""
|
|
503
|
+
if val is None:
|
|
504
|
+
return None
|
|
505
|
+
return not val # Invert: allowed -> not banned, restricted -> banned
|
|
506
|
+
|
|
507
|
+
banned_rights = types.ChatBannedRights(
|
|
508
|
+
until_date=None,
|
|
509
|
+
view_messages=False, # Never restrict viewing messages
|
|
510
|
+
send_messages=_to_banned(send_messages),
|
|
511
|
+
send_media=_to_banned(send_media),
|
|
512
|
+
send_stickers=_to_banned(send_stickers),
|
|
513
|
+
send_gifs=_to_banned(send_gifs),
|
|
514
|
+
send_games=_to_banned(send_media), # Games use media permission
|
|
515
|
+
send_inline=_to_banned(send_media), # Inline bots use media permission
|
|
516
|
+
embed_links=_to_banned(embed_links),
|
|
517
|
+
send_polls=_to_banned(send_polls),
|
|
518
|
+
change_info=_to_banned(change_info),
|
|
519
|
+
invite_users=_to_banned(invite_users),
|
|
520
|
+
pin_messages=_to_banned(pin_messages),
|
|
521
|
+
manage_topics=_to_banned(change_info), # Topics use change_info permission
|
|
522
|
+
send_photos=_to_banned(send_media),
|
|
523
|
+
send_videos=_to_banned(send_media),
|
|
524
|
+
send_roundvideos=_to_banned(send_media),
|
|
525
|
+
send_audios=_to_banned(send_media),
|
|
526
|
+
send_voices=_to_banned(send_media),
|
|
527
|
+
send_docs=_to_banned(send_media),
|
|
528
|
+
send_plain=_to_banned(send_messages),
|
|
529
|
+
)
|
|
530
|
+
|
|
531
|
+
request = functions.messages.EditChatDefaultBannedRightsRequest(
|
|
532
|
+
peer=input_peer,
|
|
533
|
+
banned_rights=banned_rights,
|
|
534
|
+
)
|
|
535
|
+
|
|
536
|
+
await client(request)
|
|
537
|
+
logger.debug(
|
|
538
|
+
"set_chat_permissions: updated default permissions for %s",
|
|
539
|
+
chat_id,
|
|
540
|
+
)
|
|
541
|
+
return True
|
|
542
|
+
|
|
543
|
+
except Exception as exc:
|
|
544
|
+
logger.error(
|
|
545
|
+
"set_chat_permissions(chat=%s) failed: %s",
|
|
546
|
+
chat_id, exc,
|
|
547
|
+
)
|
|
548
|
+
return False
|
|
549
|
+
|
|
550
|
+
async def set_chat_title(
|
|
551
|
+
self,
|
|
552
|
+
chat_id: Union[int, str],
|
|
553
|
+
title: str,
|
|
554
|
+
) -> bool:
|
|
555
|
+
"""Change the title of a channel or supergroup.
|
|
556
|
+
|
|
557
|
+
Uses ``channels.EditTitleRequest`` to update the group/channel name.
|
|
558
|
+
|
|
559
|
+
Args:
|
|
560
|
+
chat_id: Channel/supergroup identifier (int ID, @username, etc.).
|
|
561
|
+
title: New title for the chat (1–128 characters).
|
|
562
|
+
|
|
563
|
+
Returns:
|
|
564
|
+
``True`` on success, ``False`` on failure.
|
|
565
|
+
|
|
566
|
+
Example:
|
|
567
|
+
>>> await client.set_chat_title(-1001234567890, "New Group Name")
|
|
568
|
+
"""
|
|
569
|
+
client = self._get_client()
|
|
570
|
+
if not client:
|
|
571
|
+
logger.warning("set_chat_title skipped: client not connected")
|
|
572
|
+
return False
|
|
573
|
+
|
|
574
|
+
try:
|
|
575
|
+
from spluspy._engine import functions
|
|
576
|
+
except ImportError:
|
|
577
|
+
from telethon import functions
|
|
578
|
+
|
|
579
|
+
try:
|
|
580
|
+
input_channel = await self._resolve_to_input_channel(client, chat_id)
|
|
581
|
+
if input_channel is None:
|
|
582
|
+
logger.error("set_chat_title: cannot resolve %s to InputChannel", chat_id)
|
|
583
|
+
return False
|
|
584
|
+
|
|
585
|
+
request = functions.channels.EditTitleRequest(
|
|
586
|
+
channel=input_channel,
|
|
587
|
+
title=title,
|
|
588
|
+
)
|
|
589
|
+
await client(request)
|
|
590
|
+
logger.debug("set_chat_title: updated title for %s", chat_id)
|
|
591
|
+
return True
|
|
592
|
+
|
|
593
|
+
except Exception as exc:
|
|
594
|
+
logger.error("set_chat_title(chat=%s) failed: %s", chat_id, exc)
|
|
595
|
+
return False
|
|
596
|
+
|
|
597
|
+
async def set_chat_about(
|
|
598
|
+
self,
|
|
599
|
+
chat_id: Union[int, str],
|
|
600
|
+
about: str,
|
|
601
|
+
) -> bool:
|
|
602
|
+
"""Change the about/description text of a chat.
|
|
603
|
+
|
|
604
|
+
Uses ``messages.EditChatAboutRequest`` to update the chat bio.
|
|
605
|
+
Works for both basic groups and supergroups/channels.
|
|
606
|
+
|
|
607
|
+
Args:
|
|
608
|
+
chat_id: Chat identifier (int ID, @username, etc.).
|
|
609
|
+
about: New about text (0–70 characters). Pass empty string to clear.
|
|
610
|
+
|
|
611
|
+
Returns:
|
|
612
|
+
``True`` on success, ``False`` on failure.
|
|
613
|
+
|
|
614
|
+
Example:
|
|
615
|
+
>>> await client.set_chat_about(-1001234567890, "Welcome to our group!")
|
|
616
|
+
"""
|
|
617
|
+
client = self._get_client()
|
|
618
|
+
if not client:
|
|
619
|
+
logger.warning("set_chat_about skipped: client not connected")
|
|
620
|
+
return False
|
|
621
|
+
|
|
622
|
+
try:
|
|
623
|
+
from spluspy._engine import functions
|
|
624
|
+
except ImportError:
|
|
625
|
+
from telethon import functions
|
|
626
|
+
|
|
627
|
+
try:
|
|
628
|
+
input_peer = await self._resolve_to_input_peer(client, chat_id)
|
|
629
|
+
if input_peer is None:
|
|
630
|
+
# Fallback: try get_input_entity directly
|
|
631
|
+
try:
|
|
632
|
+
input_peer = await client.get_input_entity(chat_id)
|
|
633
|
+
except Exception:
|
|
634
|
+
logger.error("set_chat_about: cannot resolve %s to InputPeer", chat_id)
|
|
635
|
+
return False
|
|
636
|
+
|
|
637
|
+
request = functions.messages.EditChatAboutRequest(
|
|
638
|
+
peer=input_peer,
|
|
639
|
+
about=about,
|
|
640
|
+
)
|
|
641
|
+
await client(request)
|
|
642
|
+
logger.debug("set_chat_about: updated about for %s", chat_id)
|
|
643
|
+
return True
|
|
644
|
+
|
|
645
|
+
except Exception as exc:
|
|
646
|
+
logger.error("set_chat_about(chat=%s) failed: %s", chat_id, exc)
|
|
647
|
+
return False
|
|
648
|
+
|
|
649
|
+
async def set_chat_photo(
|
|
650
|
+
self,
|
|
651
|
+
chat_id: Union[int, str],
|
|
652
|
+
file_path: str,
|
|
653
|
+
) -> bool:
|
|
654
|
+
"""Upload and set a new photo for a channel or supergroup.
|
|
655
|
+
|
|
656
|
+
Reads the photo from *file_path*, uploads it to Telegram servers,
|
|
657
|
+
then applies it via ``channels.EditPhotoRequest``.
|
|
658
|
+
|
|
659
|
+
Args:
|
|
660
|
+
chat_id: Channel/supergroup identifier (int ID, @username, etc.).
|
|
661
|
+
file_path: Local filesystem path to the image file
|
|
662
|
+
(JPEG, PNG, or GIF recommended).
|
|
663
|
+
|
|
664
|
+
Returns:
|
|
665
|
+
``True`` on success, ``False`` on failure.
|
|
666
|
+
|
|
667
|
+
Example:
|
|
668
|
+
>>> await client.set_chat_photo(-1001234567890, "/path/to/photo.jpg")
|
|
669
|
+
"""
|
|
670
|
+
client = self._get_client()
|
|
671
|
+
if not client:
|
|
672
|
+
logger.warning("set_chat_photo skipped: client not connected")
|
|
673
|
+
return False
|
|
674
|
+
|
|
675
|
+
try:
|
|
676
|
+
from spluspy._engine import functions, types
|
|
677
|
+
except ImportError:
|
|
678
|
+
from telethon import functions, types
|
|
679
|
+
|
|
680
|
+
try:
|
|
681
|
+
input_channel = await self._resolve_to_input_channel(client, chat_id)
|
|
682
|
+
if input_channel is None:
|
|
683
|
+
logger.error("set_chat_photo: cannot resolve %s to InputChannel", chat_id)
|
|
684
|
+
return False
|
|
685
|
+
|
|
686
|
+
# Read and upload the photo file
|
|
687
|
+
import os
|
|
688
|
+
import hashlib
|
|
689
|
+
|
|
690
|
+
file_path_str = str(file_path)
|
|
691
|
+
file_size = os.path.getsize(file_path_str)
|
|
692
|
+
file_name = os.path.basename(file_path_str)
|
|
693
|
+
|
|
694
|
+
with open(file_path_str, "rb") as f:
|
|
695
|
+
file_data = f.read()
|
|
696
|
+
|
|
697
|
+
# Use client's built-in upload helper if available,
|
|
698
|
+
# otherwise do a manual upload via upload.saveFilePart
|
|
699
|
+
file_id = int.from_bytes(os.urandom(8), "big", signed=True)
|
|
700
|
+
CHUNK_SIZE = 512 * 1024 # 512 KB per part
|
|
701
|
+
total_parts = max(1, (file_size + CHUNK_SIZE - 1) // CHUNK_SIZE)
|
|
702
|
+
|
|
703
|
+
for part_idx in range(total_parts):
|
|
704
|
+
offset = part_idx * CHUNK_SIZE
|
|
705
|
+
chunk = file_data[offset:offset + CHUNK_SIZE]
|
|
706
|
+
|
|
707
|
+
if total_parts > 1:
|
|
708
|
+
result = await client(functions.upload.SaveBigFilePartRequest(
|
|
709
|
+
file_id=file_id,
|
|
710
|
+
file_part=part_idx,
|
|
711
|
+
file_total_parts=total_parts,
|
|
712
|
+
bytes=chunk,
|
|
713
|
+
))
|
|
714
|
+
else:
|
|
715
|
+
result = await client(functions.upload.SaveFilePartRequest(
|
|
716
|
+
file_id=file_id,
|
|
717
|
+
file_part=part_idx,
|
|
718
|
+
bytes=chunk,
|
|
719
|
+
))
|
|
720
|
+
|
|
721
|
+
if not result:
|
|
722
|
+
logger.error("set_chat_photo: upload failed at part %d", part_idx)
|
|
723
|
+
return False
|
|
724
|
+
|
|
725
|
+
uploaded_file = types.InputFile(
|
|
726
|
+
id=file_id,
|
|
727
|
+
parts=total_parts,
|
|
728
|
+
name=file_name,
|
|
729
|
+
md5_checksum="",
|
|
730
|
+
)
|
|
731
|
+
|
|
732
|
+
photo_obj = types.InputChatUploadedPhoto(file=uploaded_file)
|
|
733
|
+
|
|
734
|
+
request = functions.channels.EditPhotoRequest(
|
|
735
|
+
channel=input_channel,
|
|
736
|
+
photo=photo_obj,
|
|
737
|
+
)
|
|
738
|
+
await client(request)
|
|
739
|
+
logger.debug("set_chat_photo: updated photo for %s", chat_id)
|
|
740
|
+
return True
|
|
741
|
+
|
|
742
|
+
except Exception as exc:
|
|
743
|
+
logger.error("set_chat_photo(chat=%s) failed: %s", chat_id, exc)
|
|
744
|
+
return False
|
|
745
|
+
|
|
746
|
+
async def start_group_call(
|
|
747
|
+
self,
|
|
748
|
+
chat_id: Union[int, str],
|
|
749
|
+
title: Optional[str] = None,
|
|
750
|
+
) -> bool:
|
|
751
|
+
"""Start a new group call (voice chat) in a supergroup.
|
|
752
|
+
|
|
753
|
+
Uses ``phone.CreateGroupCallRequest`` to initiate the call.
|
|
754
|
+
The caller must have admin rights with ``manage_call`` enabled.
|
|
755
|
+
|
|
756
|
+
Args:
|
|
757
|
+
chat_id: Supergroup/channel identifier (int ID, @username, etc.).
|
|
758
|
+
title: Optional title for the group call (e.g. "Team Meeting").
|
|
759
|
+
|
|
760
|
+
Returns:
|
|
761
|
+
``True`` on success, ``False`` on failure.
|
|
762
|
+
|
|
763
|
+
Raises:
|
|
764
|
+
No exceptions — errors are caught and logged.
|
|
765
|
+
|
|
766
|
+
Example:
|
|
767
|
+
>>> await client.start_group_call(-1001234567890, title="Daily Standup")
|
|
768
|
+
"""
|
|
769
|
+
client = self._get_client()
|
|
770
|
+
if not client:
|
|
771
|
+
logger.warning("start_group_call skipped: client not connected")
|
|
772
|
+
return False
|
|
773
|
+
|
|
774
|
+
try:
|
|
775
|
+
from spluspy._engine import functions
|
|
776
|
+
except ImportError:
|
|
777
|
+
from telethon import functions
|
|
778
|
+
|
|
779
|
+
try:
|
|
780
|
+
input_peer = await self._resolve_to_input_peer(client, chat_id)
|
|
781
|
+
if input_peer is None:
|
|
782
|
+
# Fallback: try get_input_entity directly
|
|
783
|
+
try:
|
|
784
|
+
input_peer = await client.get_input_entity(chat_id)
|
|
785
|
+
except Exception:
|
|
786
|
+
logger.error("start_group_call: cannot resolve %s to InputPeer", chat_id)
|
|
787
|
+
return False
|
|
788
|
+
|
|
789
|
+
import os
|
|
790
|
+
random_id = int.from_bytes(os.urandom(4), "big", signed=True)
|
|
791
|
+
|
|
792
|
+
request = functions.phone.CreateGroupCallRequest(
|
|
793
|
+
peer=input_peer,
|
|
794
|
+
random_id=random_id,
|
|
795
|
+
title=title,
|
|
796
|
+
)
|
|
797
|
+
await client(request)
|
|
798
|
+
logger.debug("start_group_call: started call in %s", chat_id)
|
|
799
|
+
return True
|
|
800
|
+
|
|
801
|
+
except Exception as exc:
|
|
802
|
+
logger.error("start_group_call(chat=%s) failed: %s", chat_id, exc)
|
|
803
|
+
return False
|
|
804
|
+
|
|
805
|
+
async def stop_group_call(
|
|
806
|
+
self,
|
|
807
|
+
chat_id: Union[int, str],
|
|
808
|
+
) -> bool:
|
|
809
|
+
"""Stop (discard) an active group call in a supergroup.
|
|
810
|
+
|
|
811
|
+
Fetches the current group call via ``channels.GetFullChannelRequest``,
|
|
812
|
+
extracts the ``InputGroupCall`` from the ``full_chat.call`` attribute,
|
|
813
|
+
and sends ``phone.DiscardGroupCallRequest`` to end it.
|
|
814
|
+
|
|
815
|
+
Args:
|
|
816
|
+
chat_id: Supergroup/channel identifier (int ID, @username, etc.).
|
|
817
|
+
|
|
818
|
+
Returns:
|
|
819
|
+
``True`` on success, ``False`` on failure (including when no
|
|
820
|
+
active call exists).
|
|
821
|
+
|
|
822
|
+
Example:
|
|
823
|
+
>>> await client.stop_group_call(-1001234567890)
|
|
824
|
+
"""
|
|
825
|
+
client = self._get_client()
|
|
826
|
+
if not client:
|
|
827
|
+
logger.warning("stop_group_call skipped: client not connected")
|
|
828
|
+
return False
|
|
829
|
+
|
|
830
|
+
try:
|
|
831
|
+
from spluspy._engine import functions, types
|
|
832
|
+
except ImportError:
|
|
833
|
+
from telethon import functions, types
|
|
834
|
+
|
|
835
|
+
try:
|
|
836
|
+
input_channel = await self._resolve_to_input_channel(client, chat_id)
|
|
837
|
+
if input_channel is None:
|
|
838
|
+
logger.error("stop_group_call: cannot resolve %s to InputChannel", chat_id)
|
|
839
|
+
return False
|
|
840
|
+
|
|
841
|
+
# Fetch full channel info to get the active group call
|
|
842
|
+
full_chat_result = await client(functions.channels.GetFullChannelRequest(
|
|
843
|
+
channel=input_channel,
|
|
844
|
+
))
|
|
845
|
+
|
|
846
|
+
full_chat = getattr(full_chat_result, "full_chat", None)
|
|
847
|
+
if full_chat is None:
|
|
848
|
+
logger.warning("stop_group_call: no full_chat returned for %s", chat_id)
|
|
849
|
+
return False
|
|
850
|
+
|
|
851
|
+
input_group_call = getattr(full_chat, "call", None)
|
|
852
|
+
if input_group_call is None:
|
|
853
|
+
logger.warning("stop_group_call: no active group call in %s", chat_id)
|
|
854
|
+
return False
|
|
855
|
+
|
|
856
|
+
request = functions.phone.DiscardGroupCallRequest(
|
|
857
|
+
call=input_group_call,
|
|
858
|
+
)
|
|
859
|
+
await client(request)
|
|
860
|
+
logger.debug("stop_group_call: stopped call in %s", chat_id)
|
|
861
|
+
return True
|
|
862
|
+
|
|
863
|
+
except Exception as exc:
|
|
864
|
+
logger.error("stop_group_call(chat=%s) failed: %s", chat_id, exc)
|
|
865
|
+
return False
|
|
866
|
+
|
|
401
867
|
# ── Helper Methods ────────────────────────────────────────────
|
|
402
868
|
|
|
403
869
|
async def _resolve_to_input_channel(
|
|
@@ -545,6 +545,46 @@ class Client(MediaMixin, ChatManagementMixin):
|
|
|
545
545
|
"""Register a handler for edited messages."""
|
|
546
546
|
return self.on(MessageEditedEventBuilder(**kwargs))
|
|
547
547
|
|
|
548
|
+
def on_edited_message(self, *message_filters: Any, **kwargs: Any) -> Callable[[Handler], Handler]:
|
|
549
|
+
"""Register a handler for edited messages with optional filters.
|
|
550
|
+
|
|
551
|
+
The handler receives ``(client, message)`` as arguments.
|
|
552
|
+
Multiple filter arguments are combined with ``&`` (AND).
|
|
553
|
+
|
|
554
|
+
Example:
|
|
555
|
+
>>> @bot.on_edited_message(filters.text & filters.private)
|
|
556
|
+
... async def on_edited(client, message):
|
|
557
|
+
... await message.reply("Edited!")
|
|
558
|
+
|
|
559
|
+
>>> @bot.on_edited_message(filters.group, filters.photo)
|
|
560
|
+
... async def on_group_edit(client, message):
|
|
561
|
+
... ...
|
|
562
|
+
"""
|
|
563
|
+
from spluspy.filters.filters import Filter, AndFilter, CallableFilter
|
|
564
|
+
|
|
565
|
+
priority = kwargs.pop("priority", HandlerPriority.NORMAL)
|
|
566
|
+
builder = MessageEditedEventBuilder(**kwargs)
|
|
567
|
+
if hasattr(builder, '_priority'):
|
|
568
|
+
builder._priority = priority
|
|
569
|
+
|
|
570
|
+
composed: Any = None
|
|
571
|
+
for f in message_filters:
|
|
572
|
+
if isinstance(f, Filter):
|
|
573
|
+
composed = f if composed is None else AndFilter(composed, f)
|
|
574
|
+
elif callable(f):
|
|
575
|
+
cf = CallableFilter(f)
|
|
576
|
+
composed = cf if composed is None else AndFilter(composed, cf)
|
|
577
|
+
|
|
578
|
+
def decorator(func: Handler) -> Handler:
|
|
579
|
+
self._handlers.setdefault(MessageEdited, []).append(
|
|
580
|
+
(builder, func, composed)
|
|
581
|
+
)
|
|
582
|
+
self._handlers[MessageEdited].sort(
|
|
583
|
+
key=lambda x: x[0].priority if hasattr(x[0], 'priority') else HandlerPriority.NORMAL
|
|
584
|
+
)
|
|
585
|
+
return func
|
|
586
|
+
return decorator
|
|
587
|
+
|
|
548
588
|
def on_callback_query(self, **kwargs: Any) -> Callable[[Handler], Handler]:
|
|
549
589
|
"""Callback queries are not supported on Soroush+ user sessions.
|
|
550
590
|
|
|
@@ -740,9 +780,111 @@ class Client(MediaMixin, ChatManagementMixin):
|
|
|
740
780
|
task.add_done_callback(self._handler_tasks.discard)
|
|
741
781
|
|
|
742
782
|
client.add_event_handler(_wrapped, event)
|
|
783
|
+
elif isinstance(builder, MessageEditedEventBuilder):
|
|
784
|
+
_filter = composed_filter
|
|
785
|
+
|
|
786
|
+
sig = inspect.signature(func)
|
|
787
|
+
_param_count = len(sig.parameters)
|
|
788
|
+
|
|
789
|
+
async def _wrapped_edited(evt, _f=_filter, _pc=_param_count):
|
|
790
|
+
raw_msg = evt.message
|
|
791
|
+
|
|
792
|
+
if _f is not None and isinstance(_f, _FilterBase):
|
|
793
|
+
_lightweight = _PreMessage(raw_msg)
|
|
794
|
+
if not _f(_lightweight):
|
|
795
|
+
return
|
|
796
|
+
|
|
797
|
+
sender_id = None
|
|
798
|
+
if hasattr(raw_msg, 'sender_id'):
|
|
799
|
+
sender_id = raw_msg.sender_id
|
|
800
|
+
elif hasattr(raw_msg, 'from_id') and raw_msg.from_id:
|
|
801
|
+
fid = raw_msg.from_id
|
|
802
|
+
sender_id = getattr(fid, 'user_id', None) if hasattr(fid, 'user_id') else (fid if isinstance(fid, int) else None)
|
|
803
|
+
|
|
804
|
+
chat_id = None
|
|
805
|
+
peer = getattr(raw_msg, 'peer_id', None)
|
|
806
|
+
if peer is not None:
|
|
807
|
+
chat_id = (
|
|
808
|
+
getattr(peer, 'channel_id', None)
|
|
809
|
+
or getattr(peer, 'chat_id', None)
|
|
810
|
+
or getattr(peer, 'user_id', None)
|
|
811
|
+
)
|
|
812
|
+
if not chat_id:
|
|
813
|
+
peer_id_val = getattr(peer, 'id', None)
|
|
814
|
+
if peer_id_val:
|
|
815
|
+
constructor_name = type(peer).__name__
|
|
816
|
+
if "Channel" in constructor_name:
|
|
817
|
+
chat_id = -1000000000000 - peer_id_val
|
|
818
|
+
elif "Chat" in constructor_name:
|
|
819
|
+
chat_id = -peer_id_val
|
|
820
|
+
else:
|
|
821
|
+
chat_id = peer_id_val
|
|
822
|
+
if not chat_id:
|
|
823
|
+
prop_val = getattr(raw_msg, 'chat_id', None)
|
|
824
|
+
if prop_val and prop_val != raw_msg.id:
|
|
825
|
+
chat_id = prop_val
|
|
826
|
+
if not chat_id:
|
|
827
|
+
chat_obj = getattr(raw_msg, 'chat', None)
|
|
828
|
+
if chat_obj is not None:
|
|
829
|
+
cid = getattr(chat_obj, 'id', None)
|
|
830
|
+
if cid and cid != raw_msg.id:
|
|
831
|
+
chat_id = cid
|
|
832
|
+
if not chat_id:
|
|
833
|
+
chat_id = getattr(raw_msg, 'group_guid', None)
|
|
834
|
+
if not chat_id:
|
|
835
|
+
chat_id = 0
|
|
836
|
+
|
|
837
|
+
reply_to_id = None
|
|
838
|
+
reply_to_sender_id = None
|
|
839
|
+
_rid, _rsid = _extract_reply_fields(raw_msg)
|
|
840
|
+
reply_to_id = _rid
|
|
841
|
+
reply_to_sender_id = _rsid
|
|
842
|
+
|
|
843
|
+
media_obj = getattr(raw_msg, 'media', None)
|
|
844
|
+
fwd = getattr(raw_msg, 'fwd_from', None)
|
|
845
|
+
|
|
846
|
+
message = Message(
|
|
847
|
+
id=raw_msg.id,
|
|
848
|
+
text=getattr(raw_msg, 'message', '') or '',
|
|
849
|
+
chat_id=chat_id,
|
|
850
|
+
sender_id=sender_id,
|
|
851
|
+
is_private=getattr(raw_msg, 'is_private', True),
|
|
852
|
+
is_group=getattr(raw_msg, 'is_group', False),
|
|
853
|
+
is_channel=getattr(raw_msg, 'is_channel', False),
|
|
854
|
+
reply_to=reply_to_id,
|
|
855
|
+
reply_to_sender_id=reply_to_sender_id,
|
|
856
|
+
media=media_obj,
|
|
857
|
+
forward_origin=fwd,
|
|
858
|
+
raw=getattr(raw_msg, '__dict__', {}),
|
|
859
|
+
)
|
|
860
|
+
message._client = self
|
|
861
|
+
|
|
862
|
+
async def _run_handler() -> None:
|
|
863
|
+
try:
|
|
864
|
+
if _pc >= 2:
|
|
865
|
+
await func(self, message)
|
|
866
|
+
else:
|
|
867
|
+
splus_event = MessageEdited(message=message)
|
|
868
|
+
splus_event._set_client(self)
|
|
869
|
+
await func(splus_event)
|
|
870
|
+
except TypeError:
|
|
871
|
+
try:
|
|
872
|
+
await func(self, message)
|
|
873
|
+
except Exception:
|
|
874
|
+
splus_event = MessageEdited(message=message)
|
|
875
|
+
splus_event._set_client(self)
|
|
876
|
+
await func(splus_event)
|
|
877
|
+
except Exception as exc:
|
|
878
|
+
logger.error("Handler error: %s: %s", type(exc).__name__, exc)
|
|
879
|
+
traceback.print_exc()
|
|
880
|
+
|
|
881
|
+
task = asyncio.create_task(_run_handler())
|
|
882
|
+
self._handler_tasks.add(task)
|
|
883
|
+
task.add_done_callback(self._handler_tasks.discard)
|
|
884
|
+
|
|
885
|
+
client.add_event_handler(_wrapped_edited, se.MessageEdited())
|
|
743
886
|
else:
|
|
744
887
|
event_map = {
|
|
745
|
-
MessageEditedEventBuilder: se.MessageEdited,
|
|
746
888
|
CallbackQueryEventBuilder: se.CallbackQuery,
|
|
747
889
|
InlineQueryEventBuilder: se.InlineQuery,
|
|
748
890
|
ChatActionEventBuilder: se.ChatAction,
|