├── .flake8
├── .git-blame-ignore-revs
├── .github
├── ISSUE_TEMPLATE
│ ├── 01_question.md
│ ├── 02_enhancement.md
│ ├── 03_document.md
│ ├── 04_bug.md
│ └── config.yml
├── contributing.md
├── dependabot.yml
├── issue_template.md
├── maintainers_guide.md
├── pull_request_template.md
└── workflows
│ ├── ci-build.yml
│ ├── docs-deploy.yml
│ ├── mypy.yml
│ └── triage-issues.yml
├── .gitignore
├── .vscode
└── settings.json
├── LICENSE
├── MANIFEST.in
├── README.md
├── codecov.yml
├── docs
├── .gitignore
├── README.md
├── babel.config.js
├── content
│ ├── audit-logs.md
│ ├── index.md
│ ├── installation.md
│ ├── legacy
│ │ ├── auth.md
│ │ ├── basic_usage.md
│ │ ├── changelog.md
│ │ ├── conversations.md
│ │ ├── faq.md
│ │ ├── index.md
│ │ └── real_time_messaging.md
│ ├── oauth.md
│ ├── rtm.md
│ ├── scim.md
│ ├── socket-mode.md
│ ├── tutorial
│ │ ├── understanding-oauth-scopes.md
│ │ └── uploading-files.md
│ ├── v3-migration.md
│ ├── web.md
│ └── webhook.md
├── docusaurus.config.js
├── footerConfig.js
├── navbarConfig.js
├── package-lock.json
├── package.json
├── sidebars.js
├── src
│ ├── css
│ │ └── custom.css
│ └── theme
│ │ └── NotFound
│ │ ├── Content
│ │ └── index.js
│ │ └── index.js
└── static
│ ├── api-docs
│ └── slack_sdk
│ │ ├── aiohttp_version_checker.html
│ │ ├── audit_logs
│ │ ├── async_client.html
│ │ ├── index.html
│ │ └── v1
│ │ │ ├── async_client.html
│ │ │ ├── client.html
│ │ │ ├── index.html
│ │ │ ├── internal_utils.html
│ │ │ ├── logs.html
│ │ │ └── response.html
│ │ ├── errors
│ │ └── index.html
│ │ ├── http_retry
│ │ ├── async_handler.html
│ │ ├── builtin_async_handlers.html
│ │ ├── builtin_handlers.html
│ │ ├── builtin_interval_calculators.html
│ │ ├── handler.html
│ │ ├── index.html
│ │ ├── interval_calculator.html
│ │ ├── jitter.html
│ │ ├── request.html
│ │ ├── response.html
│ │ └── state.html
│ │ ├── index.html
│ │ ├── models
│ │ ├── attachments
│ │ │ └── index.html
│ │ ├── basic_objects.html
│ │ ├── blocks
│ │ │ ├── basic_components.html
│ │ │ ├── block_elements.html
│ │ │ ├── blocks.html
│ │ │ └── index.html
│ │ ├── dialoags.html
│ │ ├── dialogs
│ │ │ └── index.html
│ │ ├── index.html
│ │ ├── messages
│ │ │ ├── index.html
│ │ │ └── message.html
│ │ ├── metadata
│ │ │ └── index.html
│ │ └── views
│ │ │ └── index.html
│ │ ├── oauth
│ │ ├── authorize_url_generator
│ │ │ └── index.html
│ │ ├── index.html
│ │ ├── installation_store
│ │ │ ├── amazon_s3
│ │ │ │ └── index.html
│ │ │ ├── async_cacheable_installation_store.html
│ │ │ ├── async_installation_store.html
│ │ │ ├── cacheable_installation_store.html
│ │ │ ├── file
│ │ │ │ └── index.html
│ │ │ ├── index.html
│ │ │ ├── installation_store.html
│ │ │ ├── internals.html
│ │ │ ├── models
│ │ │ │ ├── bot.html
│ │ │ │ ├── index.html
│ │ │ │ └── installation.html
│ │ │ ├── sqlalchemy
│ │ │ │ └── index.html
│ │ │ └── sqlite3
│ │ │ │ └── index.html
│ │ ├── redirect_uri_page_renderer
│ │ │ └── index.html
│ │ ├── state_store
│ │ │ ├── amazon_s3
│ │ │ │ └── index.html
│ │ │ ├── async_state_store.html
│ │ │ ├── file
│ │ │ │ └── index.html
│ │ │ ├── index.html
│ │ │ ├── sqlalchemy
│ │ │ │ └── index.html
│ │ │ ├── sqlite3
│ │ │ │ └── index.html
│ │ │ └── state_store.html
│ │ ├── state_utils
│ │ │ └── index.html
│ │ └── token_rotation
│ │ │ ├── async_rotator.html
│ │ │ ├── index.html
│ │ │ └── rotator.html
│ │ ├── proxy_env_variable_loader.html
│ │ ├── rtm
│ │ ├── index.html
│ │ └── v2
│ │ │ └── index.html
│ │ ├── rtm_v2
│ │ └── index.html
│ │ ├── scim
│ │ ├── async_client.html
│ │ ├── index.html
│ │ └── v1
│ │ │ ├── async_client.html
│ │ │ ├── client.html
│ │ │ ├── default_arg.html
│ │ │ ├── group.html
│ │ │ ├── index.html
│ │ │ ├── internal_utils.html
│ │ │ ├── response.html
│ │ │ ├── types.html
│ │ │ └── user.html
│ │ ├── signature
│ │ └── index.html
│ │ ├── socket_mode
│ │ ├── aiohttp
│ │ │ └── index.html
│ │ ├── async_client.html
│ │ ├── async_listeners.html
│ │ ├── builtin
│ │ │ ├── client.html
│ │ │ ├── connection.html
│ │ │ ├── frame_header.html
│ │ │ ├── index.html
│ │ │ └── internals.html
│ │ ├── client.html
│ │ ├── index.html
│ │ ├── interval_runner.html
│ │ ├── listeners.html
│ │ ├── logger
│ │ │ ├── index.html
│ │ │ └── messages.html
│ │ ├── request.html
│ │ ├── response.html
│ │ ├── websocket_client
│ │ │ └── index.html
│ │ └── websockets
│ │ │ └── index.html
│ │ ├── version.html
│ │ ├── web
│ │ ├── async_base_client.html
│ │ ├── async_client.html
│ │ ├── async_internal_utils.html
│ │ ├── async_slack_response.html
│ │ ├── base_client.html
│ │ ├── client.html
│ │ ├── deprecation.html
│ │ ├── file_upload_v2_result.html
│ │ ├── index.html
│ │ ├── internal_utils.html
│ │ ├── legacy_base_client.html
│ │ ├── legacy_client.html
│ │ ├── legacy_slack_response.html
│ │ └── slack_response.html
│ │ └── webhook
│ │ ├── async_client.html
│ │ ├── client.html
│ │ ├── index.html
│ │ ├── internal_utils.html
│ │ └── webhook_response.html
│ └── img
│ ├── bolt-logo.svg
│ ├── bolt-py-logo.svg
│ ├── favicon.ico
│ ├── slack-logo-on-white.png
│ ├── slack-logo.svg
│ ├── understanding-oauth-approve.png
│ ├── understanding-oauth-flow.png
│ ├── upload-files-allow.png
│ ├── upload-files-bot-token.png
│ ├── upload-files-delete.png
│ ├── upload-files-first-upload.png
│ ├── upload-files-install.png
│ ├── upload-files-invite-bot.gif
│ ├── upload-files-local-file.png
│ └── upload-files-with-channel.png
├── integration_tests
├── audit_logs
│ ├── test_async_client.py
│ ├── test_client.py
│ └── test_pagination.py
├── env_variable_names.py
├── helpers.py
├── rtm
│ ├── test_issue_530.py
│ ├── test_issue_558.py
│ ├── test_issue_569.py
│ ├── test_issue_605.py
│ ├── test_issue_611.py
│ ├── test_issue_631.py
│ ├── test_issue_701.py
│ └── test_rtm_client.py
├── samples
│ ├── basic_usage
│ │ ├── calling_any_api_methods.py
│ │ ├── channels.py
│ │ ├── emoji_reactions.py
│ │ ├── rate_limits.py
│ │ ├── sending_a_message.py
│ │ ├── uploading_files.py
│ │ ├── users.py
│ │ ├── views.py
│ │ ├── views_2.py
│ │ └── views_default_to_current_conversation.py
│ ├── conversations
│ │ ├── create_private_channel.py
│ │ ├── list_conversations.py
│ │ └── open_dm.py
│ ├── issues
│ │ ├── issue_497.py
│ │ ├── issue_506.py
│ │ ├── issue_522.py
│ │ ├── issue_690.py
│ │ ├── issue_714.py
│ │ ├── issue_838.py
│ │ ├── issue_868.py
│ │ └── issue_926.py
│ ├── oauth
│ │ ├── oauth_v2.py
│ │ └── oauth_v2_async.py
│ ├── openid_connect
│ │ ├── app_manifest.yml
│ │ ├── flask_example.py
│ │ ├── requirements.txt
│ │ └── sanic_example.py
│ ├── readme
│ │ ├── async_function_in_framework.py
│ │ ├── async_script.py
│ │ ├── proxy.py
│ │ ├── rtm_client_basics.py
│ │ ├── sending_messages.py
│ │ └── uploading_files.py
│ ├── rtm_v2
│ │ ├── rtm_v2_app.py
│ │ └── rtm_v2_proxy_app.py
│ ├── scim
│ │ ├── search_groups.py
│ │ ├── search_groups_async.py
│ │ └── search_users.py
│ ├── socket_mode
│ │ ├── __init__.py
│ │ ├── aiohttp_example.py
│ │ ├── bolt_adapter
│ │ │ ├── __init__.py
│ │ │ ├── aiohttp.py
│ │ │ ├── async_base_handler.py
│ │ │ ├── async_internals.py
│ │ │ ├── base_handler.py
│ │ │ ├── builtin.py
│ │ │ ├── internals.py
│ │ │ ├── websocket_client.py
│ │ │ └── websockets.py
│ │ ├── bolt_aiohttp_async_example.py
│ │ ├── bolt_aiohttp_example.py
│ │ ├── bolt_builtin_example.py
│ │ ├── bolt_oauth_aiohttp_async_example.py
│ │ ├── bolt_oauth_aiohttp_example.py
│ │ ├── bolt_oauth_builtin_example.py
│ │ ├── bolt_oauth_websocket_client_example.py
│ │ ├── bolt_websocket_client_example.py
│ │ ├── bolt_websockets_async_example.py
│ │ ├── bolt_websockets_example.py
│ │ ├── builtin_example.py
│ │ ├── builtin_proxy_auth_example.py
│ │ ├── builtin_proxy_env_variable_example.py
│ │ ├── builtin_proxy_example.py
│ │ ├── websocket_client_example.py
│ │ ├── websocket_client_proxy_example.py
│ │ └── websockets_example.py
│ ├── token_rotation
│ │ ├── .gitignore
│ │ ├── oauth.py
│ │ ├── oauth_async.py
│ │ ├── oauth_sqlalchemy.py
│ │ ├── oauth_sqlite3.py
│ │ └── util.py
│ └── workflows
│ │ └── steps_from_apps.py
├── scim
│ ├── test_scim_client_read.py
│ └── test_scim_client_write.py
├── web
│ ├── test_admin_analytics.py
│ ├── test_admin_auth_policy.py
│ ├── test_admin_barriers.py
│ ├── test_admin_conversations.py
│ ├── test_admin_conversations_bulk.py
│ ├── test_admin_conversations_restrictAccess.py
│ ├── test_admin_conversations_retention.py
│ ├── test_admin_rate_limit_retries.py
│ ├── test_admin_roles.py
│ ├── test_admin_usergroups.py
│ ├── test_admin_users.py
│ ├── test_admin_users_session.py
│ ├── test_admin_users_session_settings.py
│ ├── test_admin_users_unsupportedVersions_export.py
│ ├── test_app_manifest.py
│ ├── test_async_web_client.py
│ ├── test_bookmarks.py
│ ├── test_calls.py
│ ├── test_canvases.py
│ ├── test_conversations_connect.py
│ ├── test_files_upload_v2.py
│ ├── test_issue_1053.py
│ ├── test_issue_1143.py
│ ├── test_issue_1305.py
│ ├── test_issue_378.py
│ ├── test_issue_480.py
│ ├── test_issue_560.py
│ ├── test_issue_594.py
│ ├── test_issue_654.py
│ ├── test_issue_670.py
│ ├── test_issue_672.py
│ ├── test_issue_677.py
│ ├── test_issue_714.py
│ ├── test_issue_728.py
│ ├── test_issue_770.py
│ ├── test_issue_809.py
│ ├── test_message_metadata.py
│ ├── test_remote_file_replacement.py
│ ├── test_team.py
│ └── test_web_client.py
└── webhook
│ ├── test_async_webhook.py
│ └── test_webhook.py
├── logs
└── .gitkeep
├── pyproject.toml
├── requirements
├── documentation.txt
├── optional.txt
└── testing.txt
├── scripts
├── build_pypi_package.sh
├── codegen.py
├── deploy_to_prod_pypi_org.sh
├── deploy_to_test_pypi_org.sh
├── generate_api_docs.sh
├── run_integration_tests.sh
├── run_mypy.sh
├── run_unit_tests.sh
├── run_validation.sh
└── uninstall_all.sh
├── setup.cfg
├── slack
├── __init__.py
├── deprecation.py
├── errors.py
├── py.typed
├── rtm
│ ├── __init__.py
│ └── client.py
├── signature
│ ├── __init__.py
│ └── verifier.py
├── version.py
├── web
│ ├── __init__.py
│ ├── async_base_client.py
│ ├── async_client.py
│ ├── async_internal_utils.py
│ ├── async_slack_response.py
│ ├── base_client.py
│ ├── classes
│ │ ├── __init__.py
│ │ ├── actions.py
│ │ ├── attachments.py
│ │ ├── blocks.py
│ │ ├── dialog_elements.py
│ │ ├── dialogs.py
│ │ ├── elements.py
│ │ ├── interactions.py
│ │ ├── messages.py
│ │ ├── objects.py
│ │ ├── readme.md
│ │ └── views.py
│ ├── client.py
│ ├── deprecation.py
│ ├── internal_utils.py
│ └── slack_response.py
└── webhook
│ ├── __init__.py
│ ├── async_client.py
│ ├── client.py
│ ├── internal_utils.py
│ └── webhook_response.py
├── slack_sdk
├── __init__.py
├── aiohttp_version_checker.py
├── audit_logs
│ ├── __init__.py
│ ├── async_client.py
│ └── v1
│ │ ├── __init__.py
│ │ ├── async_client.py
│ │ ├── client.py
│ │ ├── internal_utils.py
│ │ ├── logs.py
│ │ └── response.py
├── errors
│ └── __init__.py
├── http_retry
│ ├── __init__.py
│ ├── async_handler.py
│ ├── builtin_async_handlers.py
│ ├── builtin_handlers.py
│ ├── builtin_interval_calculators.py
│ ├── handler.py
│ ├── interval_calculator.py
│ ├── jitter.py
│ ├── request.py
│ ├── response.py
│ └── state.py
├── models
│ ├── __init__.py
│ ├── attachments
│ │ └── __init__.py
│ ├── basic_objects.py
│ ├── blocks
│ │ ├── __init__.py
│ │ ├── basic_components.py
│ │ ├── block_elements.py
│ │ └── blocks.py
│ ├── dialoags.py
│ ├── dialogs
│ │ └── __init__.py
│ ├── messages
│ │ ├── __init__.py
│ │ └── message.py
│ ├── metadata
│ │ └── __init__.py
│ └── views
│ │ └── __init__.py
├── oauth
│ ├── __init__.py
│ ├── authorize_url_generator
│ │ └── __init__.py
│ ├── installation_store
│ │ ├── __init__.py
│ │ ├── amazon_s3
│ │ │ └── __init__.py
│ │ ├── async_cacheable_installation_store.py
│ │ ├── async_installation_store.py
│ │ ├── cacheable_installation_store.py
│ │ ├── file
│ │ │ └── __init__.py
│ │ ├── installation_store.py
│ │ ├── internals.py
│ │ ├── models
│ │ │ ├── __init__.py
│ │ │ ├── bot.py
│ │ │ └── installation.py
│ │ ├── sqlalchemy
│ │ │ └── __init__.py
│ │ └── sqlite3
│ │ │ └── __init__.py
│ ├── redirect_uri_page_renderer
│ │ └── __init__.py
│ ├── state_store
│ │ ├── __init__.py
│ │ ├── amazon_s3
│ │ │ └── __init__.py
│ │ ├── async_state_store.py
│ │ ├── file
│ │ │ └── __init__.py
│ │ ├── sqlalchemy
│ │ │ └── __init__.py
│ │ ├── sqlite3
│ │ │ └── __init__.py
│ │ └── state_store.py
│ ├── state_utils
│ │ └── __init__.py
│ └── token_rotation
│ │ ├── __init__.py
│ │ ├── async_rotator.py
│ │ └── rotator.py
├── proxy_env_variable_loader.py
├── py.typed
├── rtm
│ ├── __init__.py
│ └── v2
│ │ └── __init__.py
├── rtm_v2
│ └── __init__.py
├── scim
│ ├── __init__.py
│ ├── async_client.py
│ └── v1
│ │ ├── __init__.py
│ │ ├── async_client.py
│ │ ├── client.py
│ │ ├── default_arg.py
│ │ ├── group.py
│ │ ├── internal_utils.py
│ │ ├── response.py
│ │ ├── types.py
│ │ └── user.py
├── signature
│ └── __init__.py
├── socket_mode
│ ├── __init__.py
│ ├── aiohttp
│ │ └── __init__.py
│ ├── async_client.py
│ ├── async_listeners.py
│ ├── builtin
│ │ ├── __init__.py
│ │ ├── client.py
│ │ ├── connection.py
│ │ ├── frame_header.py
│ │ └── internals.py
│ ├── client.py
│ ├── interval_runner.py
│ ├── listeners.py
│ ├── logger
│ │ ├── __init__.py
│ │ └── messages.py
│ ├── request.py
│ ├── response.py
│ ├── websocket_client
│ │ └── __init__.py
│ └── websockets
│ │ └── __init__.py
├── version.py
├── web
│ ├── __init__.py
│ ├── async_base_client.py
│ ├── async_client.py
│ ├── async_internal_utils.py
│ ├── async_slack_response.py
│ ├── base_client.py
│ ├── client.py
│ ├── deprecation.py
│ ├── file_upload_v2_result.py
│ ├── internal_utils.py
│ ├── legacy_base_client.py
│ ├── legacy_client.py
│ ├── legacy_slack_response.py
│ └── slack_response.py
└── webhook
│ ├── __init__.py
│ ├── async_client.py
│ ├── client.py
│ ├── internal_utils.py
│ └── webhook_response.py
├── tests
├── __init__.py
├── data
│ ├── channel.created.json
│ ├── im.created.json
│ ├── rtm.start.json
│ ├── slack_logo.png
│ ├── slack_logo_new.png
│ ├── view_home_001.json
│ ├── view_home_002.json
│ ├── view_home_003.json
│ ├── view_home_004.json
│ ├── view_home_005.json
│ ├── view_home_006.json
│ ├── view_modal_001.json
│ ├── view_modal_002.json
│ ├── view_modal_003.json
│ ├── view_modal_004.json
│ ├── view_modal_005.json
│ ├── view_modal_006.json
│ ├── view_modal_007.json
│ ├── view_modal_008.json
│ ├── view_modal_009.json
│ ├── view_modal_010.json
│ ├── web_response_api_test.json
│ ├── web_response_api_test_false.json
│ ├── web_response_channels_list_pagination.json
│ ├── web_response_channels_list_pagination2.json
│ ├── web_response_channels_list_pagination2_page2.json
│ ├── web_response_channels_list_pagination_has_page2.json
│ ├── web_response_channels_list_pagination_has_page3.json
│ ├── web_response_conversations_list.json
│ ├── web_response_users_list_pagination.json
│ ├── web_response_users_list_pagination_1.json
│ ├── web_response_users_setPhoto.json
│ └── 日本語.txt
├── helpers.py
├── mock_web_api_server
│ ├── __init__.py
│ ├── mock_server_thread.py
│ └── received_requests.py
├── rtm
│ ├── mock_web_api_server.py
│ ├── test_rtm_client.py
│ ├── test_rtm_client_functional.py
│ └── test_rtm_client_v2.py
├── signature
│ └── test_signature_verifier.py
├── slack_sdk
│ ├── __init__.py
│ ├── audit_logs
│ │ ├── __init__.py
│ │ ├── mock_web_api_handler.py
│ │ ├── test_client.py
│ │ ├── test_client_http_retry.py
│ │ └── test_response.py
│ ├── fatal_error_retry_handler.py
│ ├── http_retry
│ │ ├── __init__.py
│ │ └── test_builtins.py
│ ├── models
│ │ ├── __init__.py
│ │ ├── test_actions.py
│ │ ├── test_attachments.py
│ │ ├── test_blocks.py
│ │ ├── test_dialoags.py
│ │ ├── test_dialogs.py
│ │ ├── test_elements.py
│ │ ├── test_init.py
│ │ ├── test_objects.py
│ │ ├── test_options.py
│ │ └── test_views.py
│ ├── my_retry_handler.py
│ ├── oauth
│ │ ├── __init__.py
│ │ ├── authorize_url_generator
│ │ │ ├── __init__.py
│ │ │ └── test_generator.py
│ │ ├── installation_store
│ │ │ ├── __init__.py
│ │ │ ├── test_amazon_s3.py
│ │ │ ├── test_async_sqlalchemy.py
│ │ │ ├── test_file.py
│ │ │ ├── test_interface.py
│ │ │ ├── test_internals.py
│ │ │ ├── test_models.py
│ │ │ ├── test_simple_cache.py
│ │ │ ├── test_sqlalchemy.py
│ │ │ └── test_sqlite3.py
│ │ ├── redirect_uri_page_renderer
│ │ │ ├── __init__.py
│ │ │ └── test_init.py
│ │ ├── state_store
│ │ │ ├── __init__.py
│ │ │ ├── test_amazon_s3.py
│ │ │ ├── test_async_sqlalchemy.py
│ │ │ ├── test_file.py
│ │ │ ├── test_sqlalchemy.py
│ │ │ └── test_sqlite3.py
│ │ ├── state_utils
│ │ │ ├── __init__.py
│ │ │ └── test_utils.py
│ │ ├── test_init.py
│ │ └── token_rotation
│ │ │ ├── __init__.py
│ │ │ └── test_token_rotator.py
│ ├── rtm_v2
│ │ ├── __init__.py
│ │ └── test_rtm_v2.py
│ ├── scim
│ │ ├── __init__.py
│ │ ├── mock_web_api_handler.py
│ │ ├── test_client.py
│ │ ├── test_client_http_retry.py
│ │ ├── test_internals.py
│ │ └── test_response.py
│ ├── signature
│ │ ├── __init__.py
│ │ └── test_signature_verifier.py
│ ├── socket_mode
│ │ ├── __init__.py
│ │ ├── logger
│ │ │ ├── __init__.py
│ │ │ └── test_messages.py
│ │ ├── mock_socket_mode_server.py
│ │ ├── mock_web_api_handler.py
│ │ ├── test_builtin.py
│ │ ├── test_builtin_message_parser.py
│ │ ├── test_interactions_builtin.py
│ │ ├── test_interactions_websocket_client.py
│ │ ├── test_request.py
│ │ ├── test_response.py
│ │ └── test_websocket_client.py
│ ├── web
│ │ ├── __init__.py
│ │ ├── mock_web_api_handler.py
│ │ ├── mock_web_api_http_retry_handler.py
│ │ ├── test_internal_utils.py
│ │ ├── test_legacy_web_client_url_format.py
│ │ ├── test_slack_response.py
│ │ ├── test_web_client.py
│ │ ├── test_web_client_http_retry.py
│ │ ├── test_web_client_http_retry_connection.py
│ │ ├── test_web_client_http_retry_server_error.py
│ │ ├── test_web_client_issue_1049.py
│ │ ├── test_web_client_issue_829.py
│ │ ├── test_web_client_issue_891.py
│ │ ├── test_web_client_issue_900.py
│ │ ├── test_web_client_issue_921_custom_logger.py
│ │ ├── test_web_client_issue_971.py
│ │ ├── test_web_client_logger.py
│ │ ├── test_web_client_stars_deprecation.py
│ │ ├── test_web_client_url_format.py
│ │ └── test_web_client_workflow_step_deprecation.py
│ └── webhook
│ │ ├── __init__.py
│ │ ├── mock_web_api_server.py
│ │ ├── test_webhook.py
│ │ └── test_webhook_http_retry.py
├── slack_sdk_async
│ ├── __init__.py
│ ├── audit_logs
│ │ ├── __init__.py
│ │ ├── test_async_client.py
│ │ └── test_async_client_http_retry.py
│ ├── fatal_error_retry_handler.py
│ ├── helpers.py
│ ├── http_retry
│ │ ├── __init__.py
│ │ └── test_builtins.py
│ ├── my_retry_handler.py
│ ├── oauth
│ │ ├── __init__.py
│ │ ├── installation_store
│ │ │ ├── __init__.py
│ │ │ └── test_simple_cache.py
│ │ └── token_rotation
│ │ │ ├── __init__.py
│ │ │ └── test_token_rotator.py
│ ├── scim
│ │ ├── __init__.py
│ │ ├── test_async_client.py
│ │ └── test_async_client_http_retry.py
│ ├── socket_mode
│ │ ├── __init__.py
│ │ ├── test_aiohttp.py
│ │ ├── test_interactions_aiohttp.py
│ │ ├── test_interactions_websockets.py
│ │ └── test_websockets.py
│ ├── web
│ │ ├── __init__.py
│ │ ├── test_async_slack_response.py
│ │ ├── test_async_web_client.py
│ │ ├── test_async_web_client_http_retry.py
│ │ ├── test_async_web_client_logger.py
│ │ ├── test_web_client_coverage.py
│ │ ├── test_web_client_issue_829.py
│ │ ├── test_web_client_issue_891.py
│ │ ├── test_web_client_issue_921_custom_logger.py
│ │ └── test_web_client_url_format.py
│ └── webhook
│ │ ├── __init__.py
│ │ ├── test_async_webhook.py
│ │ └── test_async_webhook_http_retry.py
├── slack_sdk_fixture
│ ├── channel.created.json
│ ├── im.created.json
│ ├── rtm.start.json
│ ├── slack_logo.png
│ ├── slack_logo_new.png
│ ├── view_home_001.json
│ ├── view_home_002.json
│ ├── view_home_003.json
│ ├── view_home_004.json
│ ├── view_home_005.json
│ ├── view_home_006.json
│ ├── view_modal_001.json
│ ├── view_modal_002.json
│ ├── view_modal_003.json
│ ├── view_modal_004.json
│ ├── view_modal_005.json
│ ├── view_modal_006.json
│ ├── view_modal_007.json
│ ├── view_modal_008.json
│ ├── view_modal_009.json
│ ├── view_modal_010.json
│ ├── web_response_admin_convo_pagination.json
│ ├── web_response_admin_convo_pagination_1.json
│ ├── web_response_api_test.json
│ ├── web_response_api_test_false.json
│ ├── web_response_conversations_list.json
│ ├── web_response_conversations_list_pagination.json
│ ├── web_response_conversations_list_pagination2.json
│ ├── web_response_conversations_list_pagination2_page2.json
│ ├── web_response_conversations_list_pagination_has_page2.json
│ ├── web_response_conversations_list_pagination_has_page3.json
│ ├── web_response_fatal_error_only_once.json
│ ├── web_response_rate_limited_only_once.json
│ ├── web_response_ratelimited_only_once.json
│ ├── web_response_users_list_pagination.json
│ ├── web_response_users_list_pagination_1.json
│ ├── web_response_users_setPhoto.json
│ └── 日本語.txt
├── test_aiohttp_version_checker.py
├── test_asyncio_event_loops.py
├── test_proxy_env_variable_loader.py
├── web
│ ├── classes
│ │ ├── __init__.py
│ │ ├── test_actions.py
│ │ ├── test_attachments.py
│ │ ├── test_blocks.py
│ │ ├── test_dialogs.py
│ │ ├── test_elements.py
│ │ ├── test_init.py
│ │ ├── test_messages.py
│ │ ├── test_objects.py
│ │ └── test_views.py
│ ├── mock_web_api_handler.py
│ ├── test_async_web_client.py
│ ├── test_slack_response.py
│ ├── test_web_client.py
│ ├── test_web_client_coverage.py
│ ├── test_web_client_functional.py
│ ├── test_web_client_issue_829.py
│ ├── test_web_client_issue_891.py
│ └── test_web_client_issue_921_custom_logger.py
└── webhook
│ ├── mock_web_api_handler.py
│ ├── test_async_webhook.py
│ └── test_webhook.py
└── tutorial
├── 01-creating-the-slack-app.md
├── 02-building-a-message.md
├── 03-responding-to-slack-events.md
├── 04-running-the-app.md
├── PythOnBoardingBot
├── app.py
├── onboarding_tutorial.py
└── requirements.txt
├── README.md
└── assets
├── authorize-install.png
├── bot-token.png
├── enable-events.png
├── oauth-installation.png
├── oauth-permissions.png
├── signing-secret.png
└── subscribe-events.png
/.flake8:
--------------------------------------------------------------------------------
1 | [flake8]
2 | max-line-length = 125
--------------------------------------------------------------------------------
/.git-blame-ignore-revs:
--------------------------------------------------------------------------------
1 | # change black settings
2 | 108955b601e768fd56696be903fc8b471c73ebf7
3 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/01_question.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: SDK Question
3 | about: Submit a question about this SDK
4 | title: (Set a clear title describing your question)
5 | labels: "untriaged"
6 | assignees: ""
7 | ---
8 |
9 | (Describe your issue and goal here)
10 |
11 | ### Reproducible in:
12 |
13 | ```bash
14 | pip freeze | grep slack
15 | python --version
16 | sw_vers && uname -v # or `ver`
17 | ```
18 |
19 | #### The Slack SDK version
20 |
21 | (Paste the output of `pip freeze | grep slack`)
22 |
23 | #### Python runtime version
24 |
25 | (Paste the output of `python --version`)
26 |
27 | #### OS info
28 |
29 | (Paste the output of `sw_vers && uname -v` on macOS/Linux or `ver` on Windows OS)
30 |
31 | #### Steps to reproduce:
32 |
33 | (Share the commands to run, source code, and project settings (e.g., pyproject.toml))
34 |
35 | 1.
36 | 2.
37 | 3.
38 |
39 | ### Expected result:
40 |
41 | (Tell what you expected to happen)
42 |
43 | ### Actual result:
44 |
45 | (Tell what actually happened with logs, screenshots)
46 |
47 | ### Requirements
48 |
49 | For general questions/issues about Slack API platform or its server-side, could you submit questions at https://my.slack.com/help/requests/new instead. :bow:
50 |
51 | Please read the [Contributing guidelines](https://github.com/slackapi/python-slack-sdk/blob/main/.github/contributing.md) and [Code of Conduct](https://slackhq.github.io/code-of-conduct) before creating this issue or pull request. By submitting, you are agreeing to those rules.
52 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/02_enhancement.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: SDK Enhancement / Feature Request
3 | about: Submit an enhancement/feature request
4 | title: (Set a clear title describing your idea)
5 | labels: "untriaged"
6 | assignees: ""
7 | ---
8 |
9 | (Describe your issue and goal here)
10 |
11 | ### Category (place an `x` in each of the `[ ]`)
12 |
13 | - [ ] **slack_sdk.web.WebClient (sync/async)** (Web API client)
14 | - [ ] **slack_sdk.webhook.WebhookClient (sync/async)** (Incoming Webhook, response_url sender)
15 | - [ ] **slack_sdk.models** (UI component builders)
16 | - [ ] **slack_sdk.oauth** (OAuth Flow Utilities)
17 | - [ ] **slack_sdk.socket_mode** (Socket Mode client)
18 | - [ ] **slack_sdk.audit_logs** (Audit Logs API client)
19 | - [ ] **slack_sdk.scim** (SCIM API client)
20 | - [ ] **slack_sdk.rtm** (RTM client)
21 | - [ ] **slack_sdk.signature** (Request Signature Verifier)
22 |
23 | ### Requirements
24 |
25 | Please read the [Contributing guidelines](https://github.com/slackapi/python-slack-sdk/blob/main/.github/contributing.md) and [Code of Conduct](https://slackhq.github.io/code-of-conduct) before creating this issue or pull request. By submitting, you are agreeing to those rules.
26 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/03_document.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: SDK Document
3 | about: Submit an issue on documents
4 | title: (Set a clear title describing your idea)
5 | labels: "untriaged"
6 | assignees: ""
7 | ---
8 |
9 | (Describe your issue and goal here)
10 |
11 | ### The page URLs
12 |
13 | - https://slack.dev/python-slack-sdk/
14 |
15 | ### Requirements
16 |
17 | Please read the [Contributing guidelines](https://github.com/slackapi/python-slack-sdk/blob/main/.github/contributing.md) and [Code of Conduct](https://slackhq.github.io/code-of-conduct) before creating this issue or pull request. By submitting, you are agreeing to those rules.
18 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/04_bug.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: SDK Bug
3 | about: Report the SDK bug
4 | title: (Set a clear title describing the issue)
5 | labels: "untriaged"
6 | assignees: ""
7 | ---
8 |
9 | (Filling out the following details about bugs will help us solve your issue sooner.)
10 |
11 | ### Reproducible in:
12 |
13 | ```bash
14 | pip freeze | grep slack
15 | python --version
16 | sw_vers && uname -v # or `ver`
17 | ```
18 |
19 | #### The Slack SDK version
20 |
21 | (Paste the output of `pip freeze | grep slack`)
22 |
23 | #### Python runtime version
24 |
25 | (Paste the output of `python --version`)
26 |
27 | #### OS info
28 |
29 | (Paste the output of `sw_vers && uname -v` on macOS/Linux or `ver` on Windows OS)
30 |
31 | #### Steps to reproduce:
32 |
33 | (Share the commands to run, source code, and project settings (e.g., pyproject.toml))
34 |
35 | 1.
36 | 2.
37 | 3.
38 |
39 | ### Expected result:
40 |
41 | (Tell what you expected to happen)
42 |
43 | ### Actual result:
44 |
45 | (Tell what actually happened with logs, screenshots)
46 |
47 | ### Requirements
48 |
49 | For general questions/issues about Slack API platform or its server-side, could you submit questions at https://my.slack.com/help/requests/new instead. :bow:
50 |
51 | Please read the [Contributing guidelines](https://github.com/slackapi/python-slack-sdk/blob/main/.github/contributing.md) and [Code of Conduct](https://slackhq.github.io/code-of-conduct) before creating this issue or pull request. By submitting, you are agreeing to those rules.
52 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
2 | contact_links:
3 | - name: Slack Platform Customer Support
4 | url: https://my.slack.com/help/requests/new
5 | about: |
6 | This issue tracker is a place to track bugs, feature requests, and questions on this SDK side.
7 | If you have a general question on how to use the Slack platform, please get in touch with our customer support agents first via either /feedback in your Slack workspace or the help page link here.
8 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # Please see the documentation for all configuration options:
2 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
3 |
4 | version: 2
5 | updates:
6 | - package-ecosystem: "pip"
7 | directory: "/"
8 | schedule:
9 | interval: "monthly"
10 | open-pull-requests-limit: 5
11 | - package-ecosystem: "github-actions"
12 | directory: "/"
13 | schedule:
14 | interval: "monthly"
15 | - package-ecosystem: "npm"
16 | directory: "/docs"
17 | schedule:
18 | interval: "monthly"
19 | groups:
20 | docusaurus:
21 | patterns:
22 | - "@docusaurus/*"
23 | react:
24 | patterns:
25 | - "react"
26 | - "react-dom"
27 |
--------------------------------------------------------------------------------
/.github/issue_template.md:
--------------------------------------------------------------------------------
1 | (Describe your issue and goal here)
2 |
3 | ### Reproducible in:
4 |
5 | ```bash
6 | pip freeze | grep slack
7 | python --version
8 | sw_vers && uname -v # or `ver`
9 | ```
10 |
11 | #### The Slack SDK version
12 |
13 | (Paste the output of `pip freeze | grep slack`)
14 |
15 | #### Python runtime version
16 |
17 | (Paste the output of `python --version`)
18 |
19 | #### OS info
20 |
21 | (Paste the output of `sw_vers && uname -v` on macOS/Linux or `ver` on Windows OS)
22 |
23 | #### Steps to reproduce:
24 |
25 | (Share the commands to run, source code, and project settings (e.g., pyproject.toml))
26 |
27 | 1.
28 | 2.
29 | 3.
30 |
31 | ### Expected result:
32 |
33 | (Tell what you expected to happen)
34 |
35 | ### Actual result:
36 |
37 | (Tell what actually happened with logs, screenshots)
38 |
39 | ### Requirements
40 |
41 | For general questions/issues about Slack API platform or its server-side, could you submit questions at https://my.slack.com/help/requests/new instead. :bow:
42 |
43 | Please read the [Contributing guidelines](https://github.com/slackapi/python-slack-sdk/blob/main/.github/contributing.md) and [Code of Conduct](https://slackhq.github.io/code-of-conduct) before creating this issue or pull request. By submitting, you are agreeing to those rules.
44 |
--------------------------------------------------------------------------------
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 | ## Summary
2 |
3 |
4 |
5 | ### Testing
6 |
7 |
8 |
9 | ### Category
10 |
11 | - [ ] **slack_sdk.web.WebClient (sync/async)** (Web API client)
12 | - [ ] **slack_sdk.webhook.WebhookClient (sync/async)** (Incoming Webhook, response_url sender)
13 | - [ ] **slack_sdk.socket_mode** (Socket Mode client)
14 | - [ ] **slack_sdk.signature** (Request Signature Verifier)
15 | - [ ] **slack_sdk.oauth** (OAuth Flow Utilities)
16 | - [ ] **slack_sdk.models** (UI component builders)
17 | - [ ] **slack_sdk.scim** (SCIM API client)
18 | - [ ] **slack_sdk.audit_logs** (Audit Logs API client)
19 | - [ ] **slack_sdk.rtm_v2** (RTM client)
20 | - [ ] `/docs` (Documents)
21 | - [ ] `/tutorial` (PythOnBoardingBot tutorial)
22 | - [ ] `tests`/`integration_tests` (Automated tests for this library)
23 |
24 | ## Requirements
25 |
26 | - [ ] I've read and understood the [Contributing Guidelines](https://github.com/slackapi/python-slack-sdk/blob/main/.github/contributing.md) and have done my best effort to follow them.
27 | - [ ] I've read and agree to the [Code of Conduct](https://slackhq.github.io/code-of-conduct).
28 | - [ ] I've run `python3 -m venv .venv && source .venv/bin/activate && ./scripts/run_validation.sh` after making the changes.
29 |
--------------------------------------------------------------------------------
/.github/workflows/mypy.yml:
--------------------------------------------------------------------------------
1 | name: mypy validation
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | pull_request:
8 |
9 | jobs:
10 | build:
11 | runs-on: ubuntu-latest
12 | timeout-minutes: 5
13 | strategy:
14 | matrix:
15 | python-version: ["3.13"]
16 | permissions:
17 | contents: read
18 | steps:
19 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
20 | with:
21 | persist-credentials: false
22 | - name: Set up Python ${{ matrix.python-version }}
23 | uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
24 | with:
25 | python-version: ${{ matrix.python-version }}
26 | - name: Run mypy verification
27 | run: |
28 | ./scripts/run_mypy.sh
29 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # general things to ignore
2 | build/
3 | dist/
4 | .eggs/
5 | *.egg-info/
6 | *.egg
7 | *.py[cod]
8 | __pycache__/
9 | *.so
10 | *~
11 |
12 | # virtualenv
13 | env*/
14 | venv*/
15 | .venv*/
16 | .env*/
17 |
18 | # codecov / coverage
19 | .coverage*
20 | cov_*
21 | coverage.xml
22 | reports/
23 |
24 | # due to using tox and pytest
25 | .tox
26 | .cache
27 | .pytest_cache/
28 | .python-version
29 | pip
30 | .mypy_cache/
31 |
32 | # JetBrains PyCharm settings
33 | .idea/
34 |
35 | tmp.txt
36 | .DS_Store
37 | logs/
38 |
39 | .pytype/
40 | *.db
41 | .env*
42 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | // Place your settings in this file to overwrite default and user settings.
2 | {
3 | "python.linting.pylintEnabled": false,
4 | "python.linting.flake8Enabled": true,
5 | "python.venvPath": "${workspaceFolder}/env",
6 | "python.formatting.provider": "black",
7 | "editor.formatOnSave": true,
8 | "python.linting.enabled": true,
9 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015- Slack Technologies, LLC
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include README.md LICENSE slack/py.typed slack_sdk/py.typed
2 |
--------------------------------------------------------------------------------
/codecov.yml:
--------------------------------------------------------------------------------
1 | coverage:
2 | status:
3 | project:
4 | default:
5 | threshold: 2.0%
6 | patch:
7 | default:
8 | target: 50%
9 |
--------------------------------------------------------------------------------
/docs/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | .docusaurus
3 | build
--------------------------------------------------------------------------------
/docs/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [require.resolve('@docusaurus/core/lib/babel/preset')],
3 | };
4 |
--------------------------------------------------------------------------------
/docs/footerConfig.js:
--------------------------------------------------------------------------------
1 | const footer = {
2 | links: [
3 | {
4 | items: [
5 | {
6 | html: `
7 |
11 |
12 | ©2025 Slack Technologies, LLC, a Salesforce company. All rights reserved. Various trademarks held by their respective owners.
13 |
14 | `,
15 | },
16 | ],
17 | },
18 | ],
19 | };
20 |
21 | module.exports = footer;
22 |
--------------------------------------------------------------------------------
/docs/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "website",
3 | "version": "2024.08.01",
4 | "private": true,
5 | "scripts": {
6 | "docusaurus": "docusaurus",
7 | "start": "docusaurus start",
8 | "build": "docusaurus build",
9 | "swizzle": "docusaurus swizzle",
10 | "deploy": "docusaurus deploy",
11 | "clear": "docusaurus clear",
12 | "serve": "docusaurus serve",
13 | "write-translations": "docusaurus write-translations",
14 | "write-heading-ids": "docusaurus write-heading-ids"
15 | },
16 | "dependencies": {
17 | "@docusaurus/core": "^3.7.0",
18 | "@docusaurus/plugin-client-redirects": "^3.7.0",
19 | "@docusaurus/preset-classic": "^3.7.0",
20 | "@mdx-js/react": "^3.1.0",
21 | "clsx": "^2.0.0",
22 | "docusaurus-theme-github-codeblock": "^2.0.2",
23 | "prism-react-renderer": "^2.4.1",
24 | "react": "^19.1.0",
25 | "react-dom": "^19.1.0"
26 | },
27 | "devDependencies": {
28 | "@docusaurus/module-type-aliases": "^3.5.2",
29 | "@docusaurus/types": "^3.5.2"
30 | },
31 | "browserslist": {
32 | "production": [
33 | ">0.5%",
34 | "not dead",
35 | "not op_mini all"
36 | ],
37 | "development": [
38 | "last 3 chrome version",
39 | "last 3 firefox version",
40 | "last 5 safari version"
41 | ]
42 | },
43 | "engines": {
44 | "node": ">=20.0"
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/docs/src/theme/NotFound/Content/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import clsx from 'clsx';
3 | import Translate from '@docusaurus/Translate';
4 | import Heading from '@theme/Heading';
5 | export default function NotFoundContent({className}) {
6 | return (
7 |
8 |
9 |
10 |
11 |
14 | Oh no! There's nothing here.
15 |
16 |
17 |
18 |
21 | If we've led you astray, please let us know. We'll do our best to get things in order.
22 |
23 |
24 |
25 |
26 |
29 | For now, we suggest heading back to the beginning to get your bearings. May your next journey have clear skies to guide you true.
30 |
31 |
32 |
33 |
34 |
35 | );
36 | }
37 |
--------------------------------------------------------------------------------
/docs/src/theme/NotFound/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {translate} from '@docusaurus/Translate';
3 | import {PageMetadata} from '@docusaurus/theme-common';
4 | import Layout from '@theme/Layout';
5 | import NotFoundContent from '@theme/NotFound/Content';
6 | export default function Index() {
7 | const title = translate({
8 | id: 'theme.NotFound.title',
9 | message: 'Page Not Found',
10 | });
11 | return (
12 | <>
13 |
14 |
15 |
16 |
17 | >
18 | );
19 | }
20 |
--------------------------------------------------------------------------------
/docs/static/img/bolt-logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/docs/static/img/bolt-py-logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/static/img/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/python-slack-sdk/443ca03e425d93f47822a4de5776ff0595f5ed3d/docs/static/img/favicon.ico
--------------------------------------------------------------------------------
/docs/static/img/slack-logo-on-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/python-slack-sdk/443ca03e425d93f47822a4de5776ff0595f5ed3d/docs/static/img/slack-logo-on-white.png
--------------------------------------------------------------------------------
/docs/static/img/slack-logo.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/docs/static/img/understanding-oauth-approve.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/python-slack-sdk/443ca03e425d93f47822a4de5776ff0595f5ed3d/docs/static/img/understanding-oauth-approve.png
--------------------------------------------------------------------------------
/docs/static/img/understanding-oauth-flow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/python-slack-sdk/443ca03e425d93f47822a4de5776ff0595f5ed3d/docs/static/img/understanding-oauth-flow.png
--------------------------------------------------------------------------------
/docs/static/img/upload-files-allow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/python-slack-sdk/443ca03e425d93f47822a4de5776ff0595f5ed3d/docs/static/img/upload-files-allow.png
--------------------------------------------------------------------------------
/docs/static/img/upload-files-bot-token.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/python-slack-sdk/443ca03e425d93f47822a4de5776ff0595f5ed3d/docs/static/img/upload-files-bot-token.png
--------------------------------------------------------------------------------
/docs/static/img/upload-files-delete.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/python-slack-sdk/443ca03e425d93f47822a4de5776ff0595f5ed3d/docs/static/img/upload-files-delete.png
--------------------------------------------------------------------------------
/docs/static/img/upload-files-first-upload.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/python-slack-sdk/443ca03e425d93f47822a4de5776ff0595f5ed3d/docs/static/img/upload-files-first-upload.png
--------------------------------------------------------------------------------
/docs/static/img/upload-files-install.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/python-slack-sdk/443ca03e425d93f47822a4de5776ff0595f5ed3d/docs/static/img/upload-files-install.png
--------------------------------------------------------------------------------
/docs/static/img/upload-files-invite-bot.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/python-slack-sdk/443ca03e425d93f47822a4de5776ff0595f5ed3d/docs/static/img/upload-files-invite-bot.gif
--------------------------------------------------------------------------------
/docs/static/img/upload-files-local-file.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/python-slack-sdk/443ca03e425d93f47822a4de5776ff0595f5ed3d/docs/static/img/upload-files-local-file.png
--------------------------------------------------------------------------------
/docs/static/img/upload-files-with-channel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/python-slack-sdk/443ca03e425d93f47822a4de5776ff0595f5ed3d/docs/static/img/upload-files-with-channel.png
--------------------------------------------------------------------------------
/integration_tests/audit_logs/test_pagination.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import os
3 | import unittest
4 |
5 | from integration_tests.env_variable_names import (
6 | SLACK_SDK_TEST_GRID_ORG_ADMIN_USER_TOKEN,
7 | )
8 | from slack_sdk.audit_logs import AuditLogsClient
9 |
10 |
11 | class TestAuditLogsClient(unittest.TestCase):
12 | def setUp(self):
13 | self.client = AuditLogsClient(token=os.environ[SLACK_SDK_TEST_GRID_ORG_ADMIN_USER_TOKEN])
14 |
15 | def tearDown(self):
16 | pass
17 |
18 | def test_pagination(self):
19 | call_count = 0
20 | response = None
21 | ids = []
22 | while call_count < 10 and (response is None or response.status_code != 429):
23 | cursor = response.body["response_metadata"]["next_cursor"] if response is not None else None
24 | response = self.client.logs(action="user_login", limit=1, cursor=cursor)
25 | ids += map(lambda v: v["id"], response.body.get("entries", []))
26 | call_count += 1
27 | self.assertGreaterEqual(len(set(ids)), 10)
28 |
--------------------------------------------------------------------------------
/integration_tests/helpers.py:
--------------------------------------------------------------------------------
1 | import asyncio
2 | import inspect
3 | import sys
4 | from asyncio.events import AbstractEventLoop
5 |
6 |
7 | def async_test(coro):
8 | loop: AbstractEventLoop = asyncio.new_event_loop()
9 | asyncio.set_event_loop(loop)
10 |
11 | def wrapper(*args, **kwargs):
12 | current_loop: AbstractEventLoop = asyncio.get_event_loop()
13 | return current_loop.run_until_complete(coro(*args, **kwargs))
14 |
15 | return wrapper
16 |
17 |
18 | def is_not_specified() -> bool:
19 | # get the caller's filepath
20 | frame = inspect.stack()[1]
21 | module = inspect.getmodule(frame[0])
22 | filepath: str = module.__file__
23 |
24 | # ./scripts/run_integration_tests.sh web/test_issue_560.py
25 | test_target: str = sys.argv[1] # e.g., web/test_issue_560.py
26 | return not test_target or not filepath.endswith(test_target)
27 |
--------------------------------------------------------------------------------
/integration_tests/samples/basic_usage/calling_any_api_methods.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | logging.basicConfig(level=logging.DEBUG)
4 |
5 | # export SLACK_API_TOKEN=xoxb-***
6 | # python3 integration_tests/samples/basic_usage/calling_any_api_methods.py
7 |
8 | import os
9 | from slack_sdk.web import WebClient
10 |
11 | client = WebClient(token=os.environ["SLACK_API_TOKEN"])
12 | response = client.api_call(api_method="chat.postMessage", json={"channel": "#random", "text": "Hello world!"})
13 | assert response["message"]["text"] == "Hello world!"
14 |
--------------------------------------------------------------------------------
/integration_tests/samples/basic_usage/channels.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | logging.basicConfig(level=logging.DEBUG)
4 |
5 | # export SLACK_API_TOKEN=xoxb-***
6 | # python3 integration_tests/samples/basic_usage/channels.py
7 |
8 | import os
9 | from slack_sdk.web import WebClient
10 |
11 | client = WebClient(token=os.environ["SLACK_API_TOKEN"])
12 |
13 | response = client.conversations_list(exclude_archived=1)
14 |
15 | channel_id = response["channels"][0]["id"]
16 |
17 | response = client.conversations_info(channel=channel_id)
18 |
19 | response = client.conversations_join(channel=channel_id)
20 |
21 | response = client.conversations_leave(channel=channel_id)
22 |
23 | response = client.conversations_join(channel=channel_id)
24 |
--------------------------------------------------------------------------------
/integration_tests/samples/basic_usage/emoji_reactions.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | logging.basicConfig(level=logging.DEBUG)
4 |
5 | # export SLACK_API_TOKEN=xoxb-***
6 | # python3 integration_tests/samples/basic_usage/emoji_reactions.py
7 |
8 | import os
9 | from slack_sdk.web import WebClient
10 |
11 | client = WebClient(token=os.environ["SLACK_API_TOKEN"])
12 |
13 | if __name__ == "__main__":
14 | channel_id = "#random"
15 | user_id = client.users_list()["members"][0]["id"]
16 | else:
17 | channel_id = "C0XXXXXX"
18 | user_id = "U0XXXXXXX"
19 |
20 | response = client.chat_postMessage(channel=channel_id, text="Give me some reaction!")
21 | # Ensure the channel_id is not a name
22 | channel_id = response["channel"]
23 | ts = response["message"]["ts"]
24 |
25 | response = client.reactions_add(channel=channel_id, name="thumbsup", timestamp=ts)
26 |
27 | response = client.reactions_remove(channel=channel_id, name="thumbsup", timestamp=ts)
28 |
--------------------------------------------------------------------------------
/integration_tests/samples/basic_usage/rate_limits.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | logging.basicConfig(level=logging.DEBUG)
4 |
5 | # export SLACK_API_TOKEN=xoxb-***
6 | # python3 integration_tests/samples/basic_usage/rate_limits.py
7 |
8 | import os
9 | import time
10 | from slack_sdk.web import WebClient
11 | from slack_sdk.errors import SlackApiError
12 |
13 | client = WebClient(token=os.environ["SLACK_API_TOKEN"])
14 |
15 |
16 | # Simple wrapper for sending a Slack message
17 | def send_slack_message(channel, message):
18 | return client.chat_postMessage(channel=channel, text=message)
19 |
20 |
21 | # Make the API call and save results to `response`
22 | channel = "#random"
23 | message = "Hello, from Python!"
24 |
25 | # Do until being rate limited
26 | while True:
27 | try:
28 | response = send_slack_message(channel, message)
29 | except SlackApiError as e:
30 | if e.response["error"] == "ratelimited":
31 | # The `Retry-After` header will tell you how long to wait before retrying
32 | delay = int(e.response.headers["Retry-After"])
33 | print(f"Rate limited. Retrying in {delay} seconds")
34 | time.sleep(delay)
35 | response = send_slack_message(channel, message)
36 | else:
37 | # other errors
38 | raise e
39 |
--------------------------------------------------------------------------------
/integration_tests/samples/basic_usage/uploading_files.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | logging.basicConfig(level=logging.DEBUG)
4 |
5 | # export SLACK_API_TOKEN=xoxb-***
6 | # echo 'Hello world!' > tmp.txt
7 | # python3 integration_tests/samples/basic_usage/uploading_files.py
8 |
9 | import os
10 | from slack_sdk.web import WebClient
11 |
12 | client = WebClient(token=os.environ["SLACK_API_TOKEN"])
13 |
14 | channels = ",".join(["#random"])
15 | filepath = "./tmp.txt"
16 | response = client.files_upload(channels=channels, file=filepath)
17 | response = client.files_upload_v2(channel=response.get("file").get("channels")[0], file=filepath)
18 |
--------------------------------------------------------------------------------
/integration_tests/samples/basic_usage/users.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | logging.basicConfig(level=logging.DEBUG)
4 |
5 | # export SLACK_API_TOKEN=xoxb-***
6 | # python3 integration_tests/samples/basic_usage/users.py
7 |
8 | import os
9 | from slack_sdk.web import WebClient
10 |
11 | client = WebClient(token=os.environ["SLACK_API_TOKEN"])
12 |
13 | response = client.users_list()
14 |
--------------------------------------------------------------------------------
/integration_tests/samples/conversations/create_private_channel.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | logging.basicConfig(level=logging.DEBUG)
4 |
5 | # export SLACK_API_TOKEN=xoxb-***
6 | # python3 integration_tests/samples/conversations/create_private_channel.py
7 |
8 | import os
9 | from slack_sdk.web import WebClient
10 | from time import time
11 |
12 | client = WebClient(token=os.environ["SLACK_API_TOKEN"])
13 |
14 | channel_name = f"my-private-channel-{round(time())}"
15 | response = client.conversations_create(name=channel_name, is_private=True)
16 | channel_id = response["channel"]["id"]
17 |
18 | response = client.conversations_info(channel=channel_id, include_num_members=1) # TODO: True
19 |
20 | response = client.conversations_members(channel=channel_id)
21 | user_ids = response["members"]
22 | print(f"user_ids: {user_ids}")
23 |
24 | response = client.conversations_archive(channel=channel_id)
25 |
--------------------------------------------------------------------------------
/integration_tests/samples/conversations/list_conversations.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | logging.basicConfig(level=logging.DEBUG)
4 |
5 | # export SLACK_API_TOKEN=xoxb-***
6 | # python3 integration_tests/samples/conversations/list_conversations.py
7 |
8 | import os
9 | from slack_sdk.web import WebClient
10 |
11 | client = WebClient(token=os.environ["SLACK_API_TOKEN"])
12 |
13 | response = client.conversations_list()
14 |
15 | response = client.conversations_list(types="public_channel, private_channel")
16 |
17 | channel_id = response["channels"][0]["id"]
18 |
19 | response = client.conversations_info(channel=channel_id, include_num_members=1) # TODO: True
20 |
--------------------------------------------------------------------------------
/integration_tests/samples/conversations/open_dm.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | logging.basicConfig(level=logging.DEBUG)
4 |
5 | # export SLACK_API_TOKEN=xoxb-***
6 | # python3 integration_tests/samples/conversations/open_dm.py
7 |
8 | import os
9 | from slack_sdk.web import WebClient
10 |
11 | client = WebClient(token=os.environ["SLACK_API_TOKEN"])
12 |
13 | all_users = client.users_list(limit=100)["members"]
14 | joinable_only = (
15 | lambda u: u["id"] != "USLACKBOT"
16 | and not u["is_bot"]
17 | and not u["is_app_user"]
18 | and not u["deleted"]
19 | and not u["is_restricted"]
20 | and not u["is_ultra_restricted"]
21 | )
22 | users = filter(joinable_only, all_users)
23 | user_ids = list(map(lambda u: u["id"], users))
24 |
25 | response = client.conversations_open(users=user_ids)
26 |
--------------------------------------------------------------------------------
/integration_tests/samples/issues/issue_506.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | logging.basicConfig(level=logging.DEBUG)
4 |
5 | # export SLACK_API_TOKEN=xoxb-***
6 | # python3 integration_tests/samples/issues/issue_506.py
7 |
8 | import os
9 | from slack_sdk.rtm import RTMClient
10 |
11 | logger = logging.getLogger(__name__)
12 | global_state = {}
13 |
14 |
15 | @RTMClient.run_on(event="open")
16 | def open(**payload):
17 | web_client = payload["web_client"]
18 | auth_result = web_client.auth_test()
19 | global_state.update({"bot_id": auth_result["bot_id"]})
20 | logger.info(f"cached: {global_state}")
21 |
22 |
23 | @RTMClient.run_on(event="message")
24 | def message(**payload):
25 | data = payload["data"]
26 | if data.get("bot_id", None) == global_state["bot_id"]:
27 | logger.debug("Skipped as it's me")
28 | return
29 | # do something here
30 | web_client = payload["web_client"]
31 | message = web_client.chat_postMessage(channel=data["channel"], text="What's up?")
32 | logger.info(f"message: {message['ts']}")
33 |
34 |
35 | rtm_client = RTMClient(token=os.environ["SLACK_API_TOKEN"])
36 | rtm_client.start()
37 |
--------------------------------------------------------------------------------
/integration_tests/samples/issues/issue_690.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | logging.basicConfig(level=logging.DEBUG)
4 |
5 | # ---------------------
6 | # Flask App
7 | # ---------------------
8 |
9 | import os
10 |
11 | # pip install flask
12 | from flask import Flask, make_response, request
13 |
14 | app = Flask(__name__)
15 | logger = logging.getLogger(__name__)
16 |
17 |
18 | @app.route("/slack/oauth/callback", methods=["GET"])
19 | def endpoint():
20 | code = request.args.get("code")
21 | from slack_sdk.web import WebClient
22 | from slack_sdk.errors import SlackApiError
23 |
24 | try:
25 | client = WebClient(token="")
26 | client_id = os.environ["SLACK_CLIENT_ID"]
27 | client_secret = os.environ["SLACK_CLIENT_SECRET"]
28 | response = client.oauth_v2_access(client_id=client_id, client_secret=client_secret, code=code)
29 | result = response.get("error", "success!")
30 | return str(result)
31 | except SlackApiError as e:
32 | return make_response(str(e), 400)
33 |
34 |
35 | if __name__ == "__main__":
36 | # export SLACK_CLIENT_ID=111.222
37 | # export SLACK_CLIENT_SECRET=
38 | # FLASK_ENV=development python integration_tests/samples/issues/issue_690.py
39 | app.run(debug=True, host="localhost", port=3000)
40 |
--------------------------------------------------------------------------------
/integration_tests/samples/issues/issue_714.py:
--------------------------------------------------------------------------------
1 | import asyncio
2 | import logging
3 |
4 | logging.basicConfig(level=logging.DEBUG)
5 |
6 | logger = logging.getLogger(__name__)
7 |
8 | import os
9 | from slack_sdk.web import WebClient
10 |
11 | # export HTTPS_PROXY=http://localhost:9000
12 | client = WebClient(token=os.environ["SLACK_API_TOKEN"])
13 | response = client.auth_test()
14 | logger.info(f"HTTPS_PROXY response: {response}")
15 |
16 | client = WebClient(token=os.environ["SLACK_API_TOKEN"], proxy="http://localhost:9000")
17 | response = client.auth_test()
18 | logger.info(f"sync response: {response}")
19 |
20 | client = WebClient(token=os.environ["SLACK_API_TOKEN"], proxy="localhost:9000")
21 | response = client.auth_test()
22 | logger.info(f"sync response: {response}")
23 |
24 |
25 | async def async_call():
26 | client = WebClient(
27 | token=os.environ["SLACK_API_TOKEN"],
28 | proxy="http://localhost:9000",
29 | run_async=True,
30 | )
31 | response = await client.auth_test()
32 | logger.info(f"async response: {response}")
33 |
34 |
35 | asyncio.run(async_call())
36 |
37 | # Terminal A:
38 | # pip3 install proxy.py
39 | # proxy --port 9000 --log-level d
40 |
41 | # Terminal B:
42 | # export SLACK_API_TOKEN=xoxb-***
43 | # python3 integration_tests/samples/issues/issue_714.py
44 |
--------------------------------------------------------------------------------
/integration_tests/samples/issues/issue_838.py:
--------------------------------------------------------------------------------
1 | import json
2 | import logging
3 |
4 | logging.basicConfig(level=logging.DEBUG)
5 |
6 | # ---------------------
7 | # Slack WebClient
8 | # ---------------------
9 |
10 | import os
11 |
12 | from slack_sdk.web import WebClient
13 | from slack_sdk.signature import SignatureVerifier
14 |
15 | app_token_client = WebClient(token=os.environ["SLACK_APP_TOKEN"]) # xapp-
16 | signature_verifier = SignatureVerifier(os.environ["SLACK_SIGNING_SECRET"])
17 |
18 | # ---------------------
19 | # Flask App
20 | # ---------------------
21 |
22 | # pip3 install flask
23 | from flask import Flask, request, make_response
24 |
25 | app = Flask(__name__)
26 |
27 |
28 | @app.route("/slack/events", methods=["POST"])
29 | def slack_app():
30 | request_body = request.get_data()
31 | if not signature_verifier.is_valid_request(request_body, request.headers):
32 | return make_response("invalid request", 403)
33 |
34 | if request.headers["content-type"] == "application/json":
35 | body = json.loads(request_body)
36 | response = app_token_client.apps_event_authorizations_list(event_context=body["event_context"])
37 | print(response)
38 | return make_response("", 200)
39 |
40 | return make_response("", 404)
41 |
42 |
43 | if __name__ == "__main__":
44 | # export SLACK_SIGNING_SECRET=***
45 | # export SLACK_API_TOKEN=xoxb-***
46 | # export FLASK_ENV=development
47 | # python3 integration_tests/web/test_issue_838.py
48 | app.run("localhost", 3000)
49 |
50 | # ngrok http 3000
51 |
--------------------------------------------------------------------------------
/integration_tests/samples/issues/issue_868.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | logging.basicConfig(level=logging.DEBUG)
4 |
5 |
6 | def legacy():
7 | from slack_sdk.models.blocks import SectionBlock
8 | from slack_sdk.models.blocks.basic_components import TextObject
9 |
10 | fields = []
11 | fields.append(TextObject(text="...", type="mrkdwn"))
12 | block = SectionBlock(text="", fields=fields)
13 | assert block is not None
14 |
15 |
16 | from slack_sdk.models.blocks import SectionBlock, TextObject
17 |
18 | fields = []
19 | fields.append(TextObject(text="...", type="mrkdwn"))
20 | block = SectionBlock(text="", fields=fields)
21 | assert block is not None
22 |
23 | #
24 | # pip install mypy
25 | # mypy integration_tests/samples/issues/issue_868.py | grep integration_tests
26 | #
27 |
28 | # integration_tests/samples/issues/issue_868.py:26: error: Argument "fields" to "SectionBlock" has incompatible type "List[TextObject]"; expected "Optional[List[Union[str, Dict[Any, Any], TextObject]]]"
29 | # integration_tests/samples/issues/issue_868.py:26: note: "List" is invariant -- see http://mypy.readthedocs.io/en/latest/common_issues.html#variance
30 | # integration_tests/samples/issues/issue_868.py:26: note: Consider using "Sequence" instead, which is covariant
31 |
--------------------------------------------------------------------------------
/integration_tests/samples/issues/issue_926.py:
--------------------------------------------------------------------------------
1 | import asyncio
2 | import logging
3 | import os
4 |
5 | from slack_sdk.socket_mode.aiohttp import SocketModeClient
6 |
7 | logging.basicConfig(level=logging.DEBUG)
8 |
9 |
10 | async def main():
11 | client = SocketModeClient(app_token=os.environ["SLACK_APP_TOKEN"])
12 | await client.connect()
13 | await asyncio.sleep(3)
14 | await client.close()
15 |
16 |
17 | if __name__ == "__main__":
18 | asyncio.run(main())
19 |
20 | # The issue:
21 | # ERROR:asyncio:Unclosed client session
22 | # client_session:
23 | # INFO:slack_sdk.socket_mode.aiohttp:The session has been abandoned
24 |
--------------------------------------------------------------------------------
/integration_tests/samples/openid_connect/app_manifest.yml:
--------------------------------------------------------------------------------
1 | _metadata:
2 | major_version: 1
3 | minor_version: 1
4 | display_information:
5 | name: openid-connect-app
6 | features:
7 | app_home:
8 | home_tab_enabled: false
9 | messages_tab_enabled: true
10 | messages_tab_read_only_enabled: true
11 | oauth_config:
12 | redirect_urls:
13 | - https://{your-domain}/slack/oauth_redirect
14 | scopes:
15 | user:
16 | - openid
17 | - email
18 | - profile
19 | settings:
20 | org_deploy_enabled: false
21 | socket_mode_enabled: false
22 | token_rotation_enabled: false
23 |
--------------------------------------------------------------------------------
/integration_tests/samples/openid_connect/requirements.txt:
--------------------------------------------------------------------------------
1 | slack-sdk
2 | Flask>=2,<3
3 | pyjwt>=2.1,<3
4 | cryptography>=3.4,<4
5 | Sanic>=21.3
--------------------------------------------------------------------------------
/integration_tests/samples/readme/async_script.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | logging.basicConfig(level=logging.DEBUG)
4 |
5 | # export SLACK_API_TOKEN=xoxb-***
6 | # python3 integration_tests/samples/readme/async_script.py
7 |
8 | import asyncio
9 | import os
10 | from slack_sdk.web.async_client import AsyncWebClient
11 | from slack_sdk.errors import SlackApiError
12 |
13 | client = AsyncWebClient(token=os.environ["SLACK_API_TOKEN"])
14 | future = client.chat_postMessage(channel="#random", text="Hello world!")
15 |
16 | loop = asyncio.get_event_loop()
17 | try:
18 | # run_until_complete returns the Future's result, or raise its exception.
19 | response = loop.run_until_complete(future)
20 | assert response["message"]["text"] == "Hello world!"
21 | except SlackApiError as e:
22 | assert e.response["ok"] is False
23 | assert e.response["error"] # str like 'invalid_auth', 'channel_not_found'
24 | print(f"Got an error: {e.response['error']}")
25 | finally:
26 | loop.close()
27 |
--------------------------------------------------------------------------------
/integration_tests/samples/readme/proxy.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | logging.basicConfig(level=logging.DEBUG)
4 |
5 | # export SLACK_API_TOKEN=xoxb-***
6 | # python3 integration_tests/samples/readme/proxy.py
7 |
8 | import os
9 | from slack_sdk.web import WebClient
10 | from ssl import SSLContext
11 |
12 | sslcert = SSLContext()
13 | # pip3 install proxy.py
14 | # proxy --port 9000 --log-level d
15 | proxyinfo = "http://localhost:9000"
16 |
17 | client = WebClient(token=os.environ["SLACK_API_TOKEN"], ssl=sslcert, proxy=proxyinfo)
18 | response = client.chat_postMessage(channel="#random", text="Hello World!")
19 | print(response)
20 |
--------------------------------------------------------------------------------
/integration_tests/samples/readme/rtm_client_basics.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | logging.basicConfig(level=logging.DEBUG)
4 |
5 | # export SLACK_API_TOKEN=xoxb-***
6 | # python3 integration_tests/samples/readme/rtm_client_basics.py
7 |
8 | import os
9 | from slack_sdk.rtm import RTMClient
10 | from slack_sdk.errors import SlackApiError
11 |
12 |
13 | @RTMClient.run_on(event="message")
14 | def say_hello(**payload):
15 | data = payload["data"]
16 | web_client = payload["web_client"]
17 | rtm_client = payload["rtm_client"]
18 | if "text" in data and "Hello" in data.get("text", []):
19 | channel_id = data["channel"]
20 | thread_ts = data["ts"]
21 | user = data["user"]
22 |
23 | try:
24 | response = web_client.chat_postMessage(channel=channel_id, text=f"Hi <@{user}>!", thread_ts=thread_ts)
25 | except SlackApiError as e:
26 | # You will get a SlackApiError if "ok" is False
27 | assert e.response["ok"] is False
28 | assert e.response["error"] # str like 'invalid_auth', 'channel_not_found'
29 | print(f"Got an error: {e.response['error']}")
30 |
31 |
32 | rtm_client = RTMClient(token=os.environ["SLACK_API_TOKEN"])
33 | rtm_client.start()
34 |
--------------------------------------------------------------------------------
/integration_tests/samples/readme/sending_messages.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | logging.basicConfig(level=logging.DEBUG)
4 |
5 | # export SLACK_API_TOKEN=xoxb-***
6 | # python3 integration_tests/samples/readme/sending_messages.py
7 |
8 | import os
9 | from slack_sdk.web import WebClient
10 | from slack_sdk.errors import SlackApiError
11 |
12 | client = WebClient(token=os.environ["SLACK_API_TOKEN"])
13 |
14 | try:
15 | response = client.chat_postMessage(channel="#random", text="Hello world!")
16 | assert response["message"]["text"] == "Hello world!"
17 | except SlackApiError as e:
18 | # You will get a SlackApiError if "ok" is False
19 | assert e.response["ok"] is False
20 | assert e.response["error"] # str like 'invalid_auth', 'channel_not_found'
21 | print(f"Got an error: {e.response['error']}")
22 |
--------------------------------------------------------------------------------
/integration_tests/samples/readme/uploading_files.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | logging.basicConfig(level=logging.DEBUG)
4 |
5 | # export SLACK_API_TOKEN=xoxb-***
6 | # echo 'Hello world!' > tmp.txt
7 | # python3 integration_tests/samples/readme/uploading_files.py
8 |
9 | import os
10 | from slack_sdk.web import WebClient
11 | from slack_sdk.errors import SlackApiError
12 |
13 | client = WebClient(token=os.environ["SLACK_API_TOKEN"])
14 |
15 | try:
16 | filepath = "./tmp.txt"
17 | response = client.files_upload(channels="#random", file=filepath)
18 | assert response["file"] # the uploaded file
19 | except SlackApiError as e:
20 | # You will get a SlackApiError if "ok" is False
21 | assert e.response["ok"] is False
22 | assert e.response["error"] # str like 'invalid_auth', 'channel_not_found'
23 | print(f"Got an error: {e.response['error']}")
24 |
--------------------------------------------------------------------------------
/integration_tests/samples/rtm_v2/rtm_v2_app.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | logging.basicConfig(
4 | level=logging.DEBUG,
5 | format="%(asctime)s.%(msecs)03d %(levelname)s %(filename)s (%(lineno)s): %(message)s",
6 | datefmt="%Y-%m-%d %H:%M:%S",
7 | )
8 | logger = logging.getLogger(__name__)
9 |
10 | import os
11 | from slack_sdk.rtm.v2 import RTMClient
12 | from integration_tests.env_variable_names import SLACK_SDK_TEST_CLASSIC_APP_BOT_TOKEN
13 |
14 | if __name__ == "__main__":
15 | rtm = RTMClient(
16 | token=os.environ.get(SLACK_SDK_TEST_CLASSIC_APP_BOT_TOKEN),
17 | trace_enabled=True,
18 | all_message_trace_enabled=True,
19 | )
20 |
21 | @rtm.on("message")
22 | def handle(client: RTMClient, event: dict):
23 | client.web_client.reactions_add(
24 | channel=event["channel"],
25 | timestamp=event["ts"],
26 | name="eyes",
27 | )
28 |
29 | @rtm.on("*")
30 | def handle(client: RTMClient, event: dict):
31 | logger.info(event)
32 |
33 | rtm.start()
34 |
--------------------------------------------------------------------------------
/integration_tests/samples/rtm_v2/rtm_v2_proxy_app.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | logging.basicConfig(
4 | level=logging.DEBUG,
5 | format="%(asctime)s.%(msecs)03d %(levelname)s %(filename)s (%(lineno)s): %(message)s",
6 | datefmt="%Y-%m-%d %H:%M:%S",
7 | )
8 | logger = logging.getLogger(__name__)
9 |
10 | import os
11 | from slack_sdk.rtm.v2 import RTMClient
12 | from integration_tests.env_variable_names import SLACK_SDK_TEST_CLASSIC_APP_BOT_TOKEN
13 |
14 | # pip3 install proxy.py
15 | # proxy --port 9000 --log-level d
16 | proxy_url = "http://localhost:9000"
17 |
18 | if __name__ == "__main__":
19 | rtm = RTMClient(
20 | token=os.environ.get(SLACK_SDK_TEST_CLASSIC_APP_BOT_TOKEN),
21 | trace_enabled=True,
22 | all_message_trace_enabled=True,
23 | proxy=proxy_url,
24 | )
25 |
26 | @rtm.on("message")
27 | def handle(client: RTMClient, event: dict):
28 | client.web_client.reactions_add(
29 | channel=event["channel"],
30 | timestamp=event["ts"],
31 | name="eyes",
32 | )
33 |
34 | @rtm.on("*")
35 | def handle(client: RTMClient, event: dict):
36 | logger.info(event)
37 |
38 | rtm.start()
39 |
--------------------------------------------------------------------------------
/integration_tests/samples/scim/search_groups.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | logging.basicConfig(level=logging.DEBUG)
4 |
5 | import os
6 | from slack_sdk.scim import SCIMClient
7 |
8 | client = SCIMClient(token=os.environ["SLACK_SDK_TEST_GRID_ORG_ADMIN_USER_TOKEN"])
9 |
10 | response = client.search_groups(start_index=1, count=2)
11 | print("-----------------------")
12 | print(response.groups)
13 |
--------------------------------------------------------------------------------
/integration_tests/samples/scim/search_groups_async.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | logging.basicConfig(level=logging.DEBUG)
4 |
5 | import asyncio
6 | import os
7 | from slack_sdk.scim.async_client import AsyncSCIMClient
8 |
9 | client = AsyncSCIMClient(token=os.environ["SLACK_SDK_TEST_GRID_ORG_ADMIN_USER_TOKEN"])
10 |
11 |
12 | async def main():
13 | response = await client.search_groups(start_index=1, count=2)
14 | print("-----------------------")
15 | print(response.groups)
16 |
17 |
18 | asyncio.run(main())
19 |
--------------------------------------------------------------------------------
/integration_tests/samples/scim/search_users.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | logging.basicConfig(level=logging.DEBUG)
4 |
5 | import os
6 | from slack_sdk.scim import SCIMClient
7 |
8 | client = SCIMClient(token=os.environ["SLACK_SDK_TEST_GRID_ORG_ADMIN_USER_TOKEN"])
9 |
10 | response = client.search_users(start_index=1, count=2)
11 | print("-----------------------")
12 | print(response.users)
13 |
--------------------------------------------------------------------------------
/integration_tests/samples/socket_mode/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/python-slack-sdk/443ca03e425d93f47822a4de5776ff0595f5ed3d/integration_tests/samples/socket_mode/__init__.py
--------------------------------------------------------------------------------
/integration_tests/samples/socket_mode/aiohttp_example.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | logging.basicConfig(level=logging.DEBUG)
4 |
5 | import asyncio
6 | import os
7 | from slack_sdk.web.async_client import AsyncWebClient
8 | from slack_sdk.socket_mode.response import SocketModeResponse
9 | from slack_sdk.socket_mode.request import SocketModeRequest
10 | from slack_sdk.socket_mode.aiohttp import SocketModeClient
11 |
12 |
13 | async def main():
14 | client = SocketModeClient(
15 | app_token=os.environ.get("SLACK_SDK_TEST_SOCKET_MODE_APP_TOKEN"),
16 | web_client=AsyncWebClient(token=os.environ.get("SLACK_SDK_TEST_SOCKET_MODE_BOT_TOKEN")),
17 | trace_enabled=True,
18 | )
19 |
20 | async def process(client: SocketModeClient, req: SocketModeRequest):
21 | if req.type == "events_api":
22 | response = SocketModeResponse(envelope_id=req.envelope_id)
23 | await client.send_socket_mode_response(response)
24 | if req.payload["event"]["type"] == "message":
25 | await client.web_client.reactions_add(
26 | name="eyes",
27 | channel=req.payload["event"]["channel"],
28 | timestamp=req.payload["event"]["ts"],
29 | )
30 |
31 | client.socket_mode_request_listeners.append(process)
32 | await client.connect()
33 | await asyncio.sleep(float("inf"))
34 |
35 |
36 | asyncio.run(main())
37 |
--------------------------------------------------------------------------------
/integration_tests/samples/socket_mode/bolt_adapter/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/python-slack-sdk/443ca03e425d93f47822a4de5776ff0595f5ed3d/integration_tests/samples/socket_mode/bolt_adapter/__init__.py
--------------------------------------------------------------------------------
/integration_tests/samples/socket_mode/bolt_adapter/async_base_handler.py:
--------------------------------------------------------------------------------
1 | import asyncio
2 | import logging
3 | from typing import Union
4 |
5 | from slack_sdk.socket_mode.async_client import AsyncBaseSocketModeClient
6 | from slack_sdk.socket_mode.request import SocketModeRequest
7 |
8 | from slack_bolt import App
9 | from slack_bolt.app.async_app import AsyncApp
10 |
11 |
12 | class AsyncBaseSocketModeHandler:
13 | app: Union[App, AsyncApp] # type: ignore
14 | client: AsyncBaseSocketModeClient
15 |
16 | async def handle(self, client: AsyncBaseSocketModeClient, req: SocketModeRequest) -> None:
17 | raise NotImplementedError()
18 |
19 | async def connect_async(self):
20 | await self.client.connect()
21 |
22 | async def disconnect_async(self):
23 | await self.client.disconnect()
24 |
25 | async def close_async(self):
26 | await self.client.close()
27 |
28 | async def start_async(self):
29 | await self.connect_async()
30 | if self.app.logger.level > logging.INFO:
31 | print("⚡️ Bolt app is running!")
32 | else:
33 | self.app.logger.info("⚡️ Bolt app is running!")
34 | await asyncio.sleep(float("inf"))
35 |
--------------------------------------------------------------------------------
/integration_tests/samples/socket_mode/bolt_adapter/base_handler.py:
--------------------------------------------------------------------------------
1 | import logging
2 | from threading import Event
3 |
4 | from slack_sdk.socket_mode.client import BaseSocketModeClient
5 | from slack_sdk.socket_mode.request import SocketModeRequest
6 |
7 | from slack_bolt import App
8 |
9 |
10 | class BaseSocketModeHandler:
11 | app: App # type: ignore
12 | client: BaseSocketModeClient
13 |
14 | def handle(self, client: BaseSocketModeClient, req: SocketModeRequest) -> None:
15 | raise NotImplementedError()
16 |
17 | def connect(self):
18 | self.client.connect()
19 |
20 | def disconnect(self):
21 | self.client.disconnect()
22 |
23 | def close(self):
24 | self.client.close()
25 |
26 | def start(self):
27 | self.connect()
28 | if self.app.logger.level > logging.INFO:
29 | print("⚡️ Bolt app is running!")
30 | else:
31 | self.app.logger.info("⚡️ Bolt app is running!")
32 | Event().wait()
33 |
--------------------------------------------------------------------------------
/integration_tests/samples/socket_mode/bolt_adapter/builtin.py:
--------------------------------------------------------------------------------
1 | import os
2 | from time import time
3 | from typing import Optional
4 |
5 | from slack_sdk.socket_mode.request import SocketModeRequest
6 | from slack_sdk.socket_mode.builtin import SocketModeClient
7 |
8 | from slack_bolt import App
9 | from .base_handler import BaseSocketModeHandler
10 | from .internals import run_bolt_app, send_response
11 | from slack_bolt.response import BoltResponse
12 |
13 |
14 | class SocketModeHandler(BaseSocketModeHandler):
15 | app: App # type: ignore
16 | app_token: str
17 | client: SocketModeClient
18 |
19 | def __init__( # type: ignore
20 | self,
21 | app: App, # type: ignore
22 | app_token: Optional[str] = None,
23 | ):
24 | self.app = app
25 | self.app_token = app_token or os.environ["SLACK_APP_TOKEN"]
26 | self.client = SocketModeClient(app_token=self.app_token)
27 | self.client.socket_mode_request_listeners.append(self.handle)
28 |
29 | def handle(self, client: SocketModeClient, req: SocketModeRequest) -> None:
30 | start = time()
31 | bolt_resp: BoltResponse = run_bolt_app(self.app, req)
32 | send_response(client, req, bolt_resp, start)
33 |
--------------------------------------------------------------------------------
/integration_tests/samples/socket_mode/bolt_adapter/websocket_client.py:
--------------------------------------------------------------------------------
1 | import os
2 | from time import time
3 | from typing import Optional
4 |
5 | from slack_sdk.socket_mode.request import SocketModeRequest
6 | from slack_sdk.socket_mode.websocket_client import SocketModeClient
7 |
8 | from slack_bolt import App
9 | from .base_handler import BaseSocketModeHandler
10 | from .internals import run_bolt_app, send_response
11 | from slack_bolt.response import BoltResponse
12 |
13 |
14 | class SocketModeHandler(BaseSocketModeHandler):
15 | app: App # type: ignore
16 | app_token: str
17 | client: SocketModeClient
18 |
19 | def __init__( # type: ignore
20 | self,
21 | app: App, # type: ignore
22 | app_token: Optional[str] = None,
23 | ):
24 | self.app = app
25 | self.app_token = app_token or os.environ["SLACK_APP_TOKEN"]
26 | self.client = SocketModeClient(app_token=self.app_token)
27 | self.client.socket_mode_request_listeners.append(self.handle)
28 |
29 | def handle(self, client: SocketModeClient, req: SocketModeRequest) -> None:
30 | start = time()
31 | bolt_resp: BoltResponse = run_bolt_app(self.app, req)
32 | send_response(client, req, bolt_resp, start)
33 |
--------------------------------------------------------------------------------
/integration_tests/samples/socket_mode/bolt_aiohttp_async_example.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | logging.basicConfig(level=logging.DEBUG)
4 |
5 | import os
6 | from slack_bolt.app.async_app import AsyncApp
7 | from slack_bolt.context.async_context import AsyncBoltContext
8 |
9 | bot_token = os.environ.get("SLACK_SDK_TEST_SOCKET_MODE_BOT_TOKEN")
10 | app = AsyncApp(signing_secret="will-be-removed-soon", token=bot_token)
11 |
12 |
13 | @app.event("app_mention")
14 | async def mention(context: AsyncBoltContext):
15 | await context.say(":wave: Hi there!")
16 |
17 |
18 | @app.event("message")
19 | async def message(context: AsyncBoltContext, event: dict):
20 | await context.client.reactions_add(
21 | channel=event["channel"],
22 | timestamp=event["ts"],
23 | name="eyes",
24 | )
25 |
26 |
27 | @app.command("/hello-socket-mode")
28 | async def hello_command(ack, body):
29 | user_id = body["user_id"]
30 | await ack(f"Hi <@{user_id}>!")
31 |
32 |
33 | async def main():
34 | from bolt_adapter.aiohttp import AsyncSocketModeHandler
35 |
36 | app_token = os.environ.get("SLACK_SDK_TEST_SOCKET_MODE_APP_TOKEN")
37 | await AsyncSocketModeHandler(app, app_token).start_async()
38 |
39 |
40 | if __name__ == "__main__":
41 | import asyncio
42 |
43 | asyncio.run(main())
44 |
45 | # export SLACK_SDK_TEST_SOCKET_MODE_APP_TOKEN=
46 | # export SLACK_SDK_TEST_SOCKET_MODE_BOT_TOKEN=
47 | # pip install .[optional]
48 | # pip install slack_bolt
49 | # python integration_tests/samples/socket_mode/{this file name}.py
50 |
--------------------------------------------------------------------------------
/integration_tests/samples/socket_mode/bolt_aiohttp_example.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | logging.basicConfig(level=logging.DEBUG)
4 |
5 | import os
6 |
7 | from slack_bolt.app import App
8 | from slack_bolt.context import BoltContext
9 |
10 | bot_token = os.environ.get("SLACK_SDK_TEST_SOCKET_MODE_BOT_TOKEN")
11 | app = App(signing_secret="will-be-removed-soon", token=bot_token)
12 |
13 |
14 | @app.event("app_mention")
15 | def mention(context: BoltContext):
16 | context.say(":wave: Hi there!")
17 |
18 |
19 | @app.event("message")
20 | def message(context: BoltContext, event: dict):
21 | context.client.reactions_add(
22 | channel=event["channel"],
23 | timestamp=event["ts"],
24 | name="eyes",
25 | )
26 |
27 |
28 | @app.command("/hello-socket-mode")
29 | def hello_command(ack, body):
30 | user_id = body["user_id"]
31 | ack(f"Hi <@{user_id}>!")
32 |
33 |
34 | async def main():
35 | from bolt_adapter.aiohttp import SocketModeHandler
36 |
37 | app_token = os.environ.get("SLACK_SDK_TEST_SOCKET_MODE_APP_TOKEN")
38 | await SocketModeHandler(app, app_token).start_async()
39 |
40 |
41 | if __name__ == "__main__":
42 | import asyncio
43 |
44 | asyncio.run(main())
45 |
46 | # export SLACK_SDK_TEST_SOCKET_MODE_APP_TOKEN=
47 | # export SLACK_SDK_TEST_SOCKET_MODE_BOT_TOKEN=
48 | # pip install .[optional]
49 | # pip install slack_bolt
50 | # python integration_tests/samples/socket_mode/{this file name}.py
51 |
--------------------------------------------------------------------------------
/integration_tests/samples/socket_mode/bolt_builtin_example.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | logging.basicConfig(level=logging.DEBUG)
4 |
5 | import os
6 |
7 | from slack_bolt.app import App
8 | from slack_bolt.context import BoltContext
9 |
10 | bot_token = os.environ.get("SLACK_SDK_TEST_SOCKET_MODE_BOT_TOKEN")
11 | app = App(signing_secret="will-be-removed-soon", token=bot_token)
12 |
13 |
14 | @app.event("app_mention")
15 | def mention(context: BoltContext):
16 | context.say(":wave: Hi there!")
17 |
18 |
19 | @app.event("message")
20 | def message(context: BoltContext, event: dict):
21 | context.client.reactions_add(
22 | channel=event["channel"],
23 | timestamp=event["ts"],
24 | name="eyes",
25 | )
26 |
27 |
28 | @app.command("/hello-socket-mode")
29 | def hello_command(ack, body):
30 | user_id = body["user_id"]
31 | ack(f"Hi <@{user_id}>!")
32 |
33 |
34 | if __name__ == "__main__":
35 | from bolt_adapter.builtin import SocketModeHandler
36 |
37 | app_token = os.environ.get("SLACK_SDK_TEST_SOCKET_MODE_APP_TOKEN")
38 | SocketModeHandler(app, app_token).start()
39 |
40 | # export SLACK_SDK_TEST_SOCKET_MODE_APP_TOKEN=
41 | # export SLACK_SDK_TEST_SOCKET_MODE_BOT_TOKEN=
42 | # pip install .[optional]
43 | # pip install slack_bolt
44 | # python integration_tests/samples/socket_mode/{this file name}.py
45 |
--------------------------------------------------------------------------------
/integration_tests/samples/socket_mode/bolt_websocket_client_example.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | logging.basicConfig(level=logging.DEBUG)
4 |
5 | import os
6 |
7 | from slack_bolt.app import App
8 | from slack_bolt.context import BoltContext
9 |
10 | bot_token = os.environ.get("SLACK_SDK_TEST_SOCKET_MODE_BOT_TOKEN")
11 | app = App(signing_secret="will-be-removed-soon", token=bot_token)
12 |
13 |
14 | @app.event("app_mention")
15 | def mention(context: BoltContext):
16 | context.say(":wave: Hi there!")
17 |
18 |
19 | @app.event("message")
20 | def message(context: BoltContext, event: dict):
21 | context.client.reactions_add(
22 | channel=event["channel"],
23 | timestamp=event["ts"],
24 | name="eyes",
25 | )
26 |
27 |
28 | @app.command("/hello-socket-mode")
29 | def hello_command(ack, body):
30 | user_id = body["user_id"]
31 | ack(f"Hi <@{user_id}>!")
32 |
33 |
34 | if __name__ == "__main__":
35 | from bolt_adapter.websocket_client import SocketModeHandler
36 |
37 | app_token = os.environ.get("SLACK_SDK_TEST_SOCKET_MODE_APP_TOKEN")
38 | SocketModeHandler(app, app_token).start()
39 |
40 | # export SLACK_SDK_TEST_SOCKET_MODE_APP_TOKEN=
41 | # export SLACK_SDK_TEST_SOCKET_MODE_BOT_TOKEN=
42 | # pip install .[optional]
43 | # pip install slack_bolt
44 | # python integration_tests/samples/socket_mode/{this file name}.py
45 |
--------------------------------------------------------------------------------
/integration_tests/samples/socket_mode/bolt_websockets_async_example.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | logging.basicConfig(level=logging.DEBUG)
4 |
5 | import os
6 | from slack_bolt.app.async_app import AsyncApp
7 | from slack_bolt.context.async_context import AsyncBoltContext
8 |
9 | bot_token = os.environ.get("SLACK_SDK_TEST_SOCKET_MODE_BOT_TOKEN")
10 | app = AsyncApp(signing_secret="will-be-removed-soon", token=bot_token)
11 |
12 |
13 | @app.event("app_mention")
14 | async def mention(context: AsyncBoltContext):
15 | await context.say(":wave: Hi there!")
16 |
17 |
18 | @app.event("message")
19 | async def message(context: AsyncBoltContext, event: dict):
20 | await context.client.reactions_add(
21 | channel=event["channel"],
22 | timestamp=event["ts"],
23 | name="eyes",
24 | )
25 |
26 |
27 | @app.command("/hello-socket-mode")
28 | async def hello_command(ack, body):
29 | user_id = body["user_id"]
30 | await ack(f"Hi <@{user_id}>!")
31 |
32 |
33 | async def main():
34 | from bolt_adapter.websockets import AsyncSocketModeHandler
35 |
36 | app_token = os.environ.get("SLACK_SDK_TEST_SOCKET_MODE_APP_TOKEN")
37 | await AsyncSocketModeHandler(app, app_token).start_async()
38 |
39 |
40 | if __name__ == "__main__":
41 | import asyncio
42 |
43 | asyncio.run(main())
44 |
45 | # export SLACK_SDK_TEST_SOCKET_MODE_APP_TOKEN=
46 | # export SLACK_SDK_TEST_SOCKET_MODE_BOT_TOKEN=
47 | # pip install .[optional]
48 | # pip install slack_bolt
49 | # python integration_tests/samples/socket_mode/{this file name}.py
50 |
--------------------------------------------------------------------------------
/integration_tests/samples/socket_mode/bolt_websockets_example.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | logging.basicConfig(level=logging.DEBUG)
4 |
5 | import os
6 |
7 | from slack_bolt.app import App
8 | from slack_bolt.context import BoltContext
9 |
10 | bot_token = os.environ.get("SLACK_SDK_TEST_SOCKET_MODE_BOT_TOKEN")
11 | app = App(signing_secret="will-be-removed-soon", token=bot_token)
12 |
13 |
14 | @app.event("app_mention")
15 | def mention(context: BoltContext):
16 | context.say(":wave: Hi there!")
17 |
18 |
19 | @app.event("message")
20 | def message(context: BoltContext, event: dict):
21 | context.client.reactions_add(
22 | channel=event["channel"],
23 | timestamp=event["ts"],
24 | name="eyes",
25 | )
26 |
27 |
28 | @app.command("/hello-socket-mode")
29 | def hello_command(ack, body):
30 | user_id = body["user_id"]
31 | ack(f"Hi <@{user_id}>!")
32 |
33 |
34 | async def main():
35 | from bolt_adapter.websockets import SocketModeHandler
36 |
37 | app_token = os.environ.get("SLACK_SDK_TEST_SOCKET_MODE_APP_TOKEN")
38 | await SocketModeHandler(app, app_token).start_async()
39 |
40 |
41 | if __name__ == "__main__":
42 | import asyncio
43 |
44 | asyncio.run(main())
45 |
46 | # export SLACK_SDK_TEST_SOCKET_MODE_APP_TOKEN=
47 | # export SLACK_SDK_TEST_SOCKET_MODE_BOT_TOKEN=
48 | # pip install .[optional]
49 | # pip install slack_bolt
50 | # python integration_tests/samples/socket_mode/{this file name}.py
51 |
--------------------------------------------------------------------------------
/integration_tests/samples/socket_mode/builtin_example.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | logging.basicConfig(
4 | level=logging.DEBUG,
5 | format="%(asctime)s.%(msecs)03d %(levelname)s %(pathname)s (%(lineno)s): %(message)s",
6 | datefmt="%Y-%m-%d %H:%M:%S",
7 | )
8 |
9 | import os
10 | from threading import Event
11 | from slack_sdk.web import WebClient
12 | from slack_sdk.socket_mode.response import SocketModeResponse
13 | from slack_sdk.socket_mode.request import SocketModeRequest
14 | from slack_sdk.socket_mode import SocketModeClient
15 |
16 | client = SocketModeClient(
17 | app_token=os.environ.get("SLACK_SDK_TEST_SOCKET_MODE_APP_TOKEN"),
18 | web_client=WebClient(token=os.environ.get("SLACK_SDK_TEST_SOCKET_MODE_BOT_TOKEN")),
19 | trace_enabled=True,
20 | all_message_trace_enabled=True,
21 | )
22 |
23 | if __name__ == "__main__":
24 |
25 | def process(client: SocketModeClient, req: SocketModeRequest):
26 | if req.type == "events_api":
27 | response = SocketModeResponse(envelope_id=req.envelope_id)
28 | client.send_socket_mode_response(response)
29 | client.web_client.reactions_add(
30 | name="eyes",
31 | channel=req.payload["event"]["channel"],
32 | timestamp=req.payload["event"]["ts"],
33 | )
34 |
35 | client.socket_mode_request_listeners.append(process)
36 | client.connect()
37 | Event().wait()
38 |
--------------------------------------------------------------------------------
/integration_tests/samples/socket_mode/websocket_client_example.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | logging.basicConfig(level=logging.DEBUG)
4 |
5 | import os
6 | from threading import Event
7 | from slack_sdk.web import WebClient
8 | from slack_sdk.socket_mode.response import SocketModeResponse
9 | from slack_sdk.socket_mode.request import SocketModeRequest
10 | from slack_sdk.socket_mode.websocket_client import SocketModeClient
11 |
12 | client = SocketModeClient(
13 | app_token=os.environ.get("SLACK_SDK_TEST_SOCKET_MODE_APP_TOKEN"),
14 | web_client=WebClient(token=os.environ.get("SLACK_SDK_TEST_SOCKET_MODE_BOT_TOKEN")),
15 | trace_enabled=True,
16 | )
17 |
18 | if __name__ == "__main__":
19 |
20 | def process(client: SocketModeClient, req: SocketModeRequest):
21 | if req.type == "events_api":
22 | response = SocketModeResponse(envelope_id=req.envelope_id)
23 | client.send_socket_mode_response(response)
24 | client.web_client.reactions_add(
25 | name="eyes",
26 | channel=req.payload["event"]["channel"],
27 | timestamp=req.payload["event"]["ts"],
28 | )
29 |
30 | client.socket_mode_request_listeners.append(process)
31 | client.connect()
32 | Event().wait()
33 |
--------------------------------------------------------------------------------
/integration_tests/samples/socket_mode/websocket_client_proxy_example.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | logging.basicConfig(level=logging.DEBUG)
4 |
5 | import os
6 | from threading import Event
7 | from slack_sdk.web import WebClient
8 | from slack_sdk.socket_mode.response import SocketModeResponse
9 | from slack_sdk.socket_mode.request import SocketModeRequest
10 | from slack_sdk.socket_mode.websocket_client import SocketModeClient
11 |
12 | client = SocketModeClient(
13 | app_token=os.environ.get("SLACK_SDK_TEST_SOCKET_MODE_APP_TOKEN"),
14 | web_client=WebClient(
15 | token=os.environ.get("SLACK_SDK_TEST_SOCKET_MODE_BOT_TOKEN"),
16 | # pip3 install proxy.py
17 | # proxy --port 9000 --log-level d
18 | proxy="http://localhost:9000",
19 | ),
20 | trace_enabled=True,
21 | # pip3 install proxy.py
22 | # proxy --port 9000 --log-level d
23 | http_proxy_host="localhost",
24 | http_proxy_port=9000,
25 | http_proxy_auth=("user", "pass"),
26 | )
27 |
28 | if __name__ == "__main__":
29 |
30 | def process(client: SocketModeClient, req: SocketModeRequest):
31 | if req.type == "events_api":
32 | response = SocketModeResponse(envelope_id=req.envelope_id)
33 | client.send_socket_mode_response(response)
34 | client.web_client.reactions_add(
35 | name="eyes",
36 | channel=req.payload["event"]["channel"],
37 | timestamp=req.payload["event"]["ts"],
38 | )
39 |
40 | client.socket_mode_request_listeners.append(process)
41 | client.connect()
42 | Event().wait()
43 |
--------------------------------------------------------------------------------
/integration_tests/samples/socket_mode/websockets_example.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | logging.basicConfig(level=logging.DEBUG)
4 |
5 | import asyncio
6 | import os
7 | from slack_sdk.web.async_client import AsyncWebClient
8 | from slack_sdk.socket_mode.response import SocketModeResponse
9 | from slack_sdk.socket_mode.request import SocketModeRequest
10 | from slack_sdk.socket_mode.websockets import SocketModeClient
11 |
12 |
13 | async def main():
14 | client = SocketModeClient(
15 | app_token=os.environ.get("SLACK_SDK_TEST_SOCKET_MODE_APP_TOKEN"),
16 | web_client=AsyncWebClient(token=os.environ.get("SLACK_SDK_TEST_SOCKET_MODE_BOT_TOKEN")),
17 | )
18 |
19 | async def process(client: SocketModeClient, req: SocketModeRequest):
20 | if req.type == "events_api":
21 | response = SocketModeResponse(envelope_id=req.envelope_id)
22 | await client.send_socket_mode_response(response)
23 |
24 | await client.web_client.reactions_add(
25 | name="eyes",
26 | channel=req.payload["event"]["channel"],
27 | timestamp=req.payload["event"]["ts"],
28 | )
29 |
30 | client.socket_mode_request_listeners.append(process)
31 | await client.connect()
32 | await asyncio.sleep(float("inf"))
33 |
34 |
35 | asyncio.run(main())
36 |
--------------------------------------------------------------------------------
/integration_tests/samples/token_rotation/.gitignore:
--------------------------------------------------------------------------------
1 | .env*
--------------------------------------------------------------------------------
/integration_tests/web/test_admin_roles.py:
--------------------------------------------------------------------------------
1 | import asyncio
2 | import logging
3 | import os
4 | import time
5 | import unittest
6 |
7 | from integration_tests.env_variable_names import (
8 | SLACK_SDK_TEST_GRID_ORG_ADMIN_USER_TOKEN,
9 | SLACK_SDK_TEST_GRID_IDP_USERGROUP_ID,
10 | SLACK_SDK_TEST_GRID_TEAM_ID,
11 | SLACK_SDK_TEST_GRID_USER_ID,
12 | )
13 | from integration_tests.helpers import async_test
14 | from slack_sdk.web import WebClient
15 | from slack_sdk.web.async_client import AsyncWebClient
16 |
17 |
18 | class TestWebClient(unittest.TestCase):
19 | """Runs integration tests with real Slack API"""
20 |
21 | def setUp(self):
22 | self.org_admin_token = os.environ[SLACK_SDK_TEST_GRID_ORG_ADMIN_USER_TOKEN]
23 |
24 | def tearDown(self):
25 | pass
26 |
27 | def test_sync(self):
28 | client: WebClient = WebClient(token=self.org_admin_token)
29 | list_response = client.admin_roles_listAssignments(role_ids=["Rl0A"], limit=3, sort_dir="DESC")
30 | self.assertGreater(len(list_response.get("role_assignments", [])), 0)
31 | # TODO tests for add/remove
32 |
33 | @async_test
34 | async def test_async(self):
35 | client: AsyncWebClient = AsyncWebClient(token=self.org_admin_token)
36 | list_response = await client.admin_roles_listAssignments(role_ids=["Rl0A"], limit=3, sort_dir="DESC")
37 | self.assertGreater(len(list_response.get("role_assignments", [])), 0)
38 | # TODO tests for add/remove
39 |
--------------------------------------------------------------------------------
/integration_tests/web/test_admin_users_unsupportedVersions_export.py:
--------------------------------------------------------------------------------
1 | import os
2 | import unittest
3 | import time
4 |
5 | from integration_tests.env_variable_names import (
6 | SLACK_SDK_TEST_GRID_ORG_ADMIN_USER_TOKEN,
7 | )
8 | from slack_sdk.web import WebClient
9 |
10 |
11 | class TestWebClient(unittest.TestCase):
12 | def setUp(self):
13 | self.org_admin_token = os.environ[SLACK_SDK_TEST_GRID_ORG_ADMIN_USER_TOKEN]
14 | self.client: WebClient = WebClient(token=self.org_admin_token)
15 |
16 | def tearDown(self):
17 | pass
18 |
19 | def test_no_args(self):
20 | response = self.client.admin_users_unsupportedVersions_export()
21 | self.assertIsNone(response.get("error"))
22 |
23 | def test_full_args(self):
24 | response = self.client.admin_users_unsupportedVersions_export(
25 | date_end_of_support=int(round(time.time())) + 60 * 60 * 24 * 120,
26 | date_sessions_started=0,
27 | )
28 | self.assertIsNone(response.get("error"))
29 |
30 | def test_full_args_str(self):
31 | response = self.client.admin_users_unsupportedVersions_export(
32 | date_end_of_support=str(int(round(time.time())) + 60 * 60 * 24 * 120),
33 | date_sessions_started="0",
34 | )
35 | self.assertIsNone(response.get("error"))
36 |
--------------------------------------------------------------------------------
/integration_tests/web/test_issue_1143.py:
--------------------------------------------------------------------------------
1 | import os
2 | import unittest
3 |
4 | from integration_tests.env_variable_names import SLACK_SDK_TEST_BOT_TOKEN
5 | from integration_tests.helpers import async_test
6 | from slack_sdk.errors import SlackApiError
7 | from slack_sdk.web import WebClient
8 | from slack_sdk.web.async_client import AsyncWebClient
9 |
10 |
11 | class TestWebClient(unittest.TestCase):
12 | """Runs integration tests with real Slack API
13 |
14 | export SLACK_SDK_TEST_BOT_TOKEN=xoxb-xxx
15 | ./scripts/run_integration_tests.sh integration_tests/web/test_issue_1143.py
16 |
17 | https://github.com/slackapi/python-slack-sdk/issues/1143
18 | """
19 |
20 | def setUp(self):
21 | self.bot_token = os.environ[SLACK_SDK_TEST_BOT_TOKEN]
22 |
23 | def tearDown(self):
24 | pass
25 |
26 | def test_backward_compatible_header(self):
27 | client: WebClient = WebClient(token=self.bot_token)
28 | try:
29 | while True:
30 | client.users_list()
31 | except SlackApiError as e:
32 | self.assertIsNotNone(e.response.headers["Retry-After"])
33 |
34 | @async_test
35 | async def test_backward_compatible_header_async(self):
36 | client: AsyncWebClient = AsyncWebClient(token=self.bot_token)
37 | try:
38 | while True:
39 | await client.users_list()
40 | except SlackApiError as e:
41 | self.assertIsNotNone(e.response.headers["Retry-After"])
42 |
--------------------------------------------------------------------------------
/integration_tests/web/test_issue_378.py:
--------------------------------------------------------------------------------
1 | import asyncio
2 | import logging
3 | import os
4 | import unittest
5 |
6 | from integration_tests.env_variable_names import SLACK_SDK_TEST_USER_TOKEN
7 | from integration_tests.helpers import async_test
8 | from slack_sdk.web import WebClient
9 | from slack_sdk.web.async_client import AsyncWebClient
10 |
11 |
12 | class TestWebClient(unittest.TestCase):
13 | """Runs integration tests with real Slack API
14 |
15 | https://github.com/slackapi/python-slack-sdk/issues/378
16 | """
17 |
18 | def setUp(self):
19 | self.logger = logging.getLogger(__name__)
20 | self.user_token = os.environ[SLACK_SDK_TEST_USER_TOKEN]
21 | self.sync_client: WebClient = WebClient(token=self.user_token)
22 | self.async_client: AsyncWebClient = AsyncWebClient(token=self.user_token)
23 |
24 | def tearDown(self):
25 | pass
26 |
27 | def test_issue_378(self):
28 | client = self.sync_client
29 | response = client.users_setPhoto(image="tests/data/slack_logo_new.png")
30 | self.assertIsNotNone(response)
31 |
32 | @async_test
33 | async def test_issue_378_async(self):
34 | client = self.async_client
35 | response = await client.users_setPhoto(image="tests/data/slack_logo_new.png")
36 | self.assertIsNotNone(response)
37 |
--------------------------------------------------------------------------------
/integration_tests/web/test_issue_714.py:
--------------------------------------------------------------------------------
1 | import asyncio
2 | import os
3 | import unittest
4 | from urllib.error import URLError
5 |
6 | from aiohttp import ClientConnectorError
7 |
8 | from integration_tests.env_variable_names import SLACK_SDK_TEST_BOT_TOKEN
9 | from integration_tests.helpers import async_test
10 | from slack_sdk.web import WebClient
11 | from slack_sdk.web.async_client import AsyncWebClient
12 |
13 |
14 | class TestWebClient(unittest.TestCase):
15 | def setUp(self):
16 | self.proxy = "http://invalid-host:9999"
17 | self.bot_token = os.environ[SLACK_SDK_TEST_BOT_TOKEN]
18 |
19 | def tearDown(self):
20 | pass
21 |
22 | def test_proxy_failure(self):
23 | client: WebClient = WebClient(token=self.bot_token, proxy=self.proxy)
24 | with self.assertRaises(URLError):
25 | client.auth_test()
26 |
27 | @async_test
28 | async def test_proxy_failure_async(self):
29 | client: AsyncWebClient = AsyncWebClient(token=self.bot_token, proxy=self.proxy)
30 | with self.assertRaises(ClientConnectorError):
31 | await client.auth_test()
32 |
--------------------------------------------------------------------------------
/integration_tests/web/test_issue_809.py:
--------------------------------------------------------------------------------
1 | import asyncio
2 | import io
3 | import logging
4 | import os
5 | import unittest
6 |
7 | from integration_tests.env_variable_names import SLACK_SDK_TEST_BOT_TOKEN
8 | from integration_tests.helpers import async_test
9 | from slack_sdk.web import WebClient
10 | from slack_sdk.web.async_client import AsyncWebClient
11 |
12 |
13 | class TestWebClient(unittest.TestCase):
14 | """Runs integration tests with real Slack API
15 |
16 | https://github.com/slackapi/python-slack-sdk/issues/809
17 | """
18 |
19 | def setUp(self):
20 | self.logger = logging.getLogger(__name__)
21 | self.bot_token = os.environ[SLACK_SDK_TEST_BOT_TOKEN]
22 | self.sync_client: WebClient = WebClient(token=self.bot_token)
23 | self.async_client: AsyncWebClient = AsyncWebClient(token=self.bot_token)
24 |
25 | def tearDown(self):
26 | pass
27 |
28 | def test_issue_809(self):
29 | client = self.sync_client
30 | buff = io.BytesIO(b"here is my data but not sure what is wrong.......")
31 | buff.seek(0)
32 | upload = client.files_upload(file=buff)
33 | self.assertIsNotNone(upload)
34 |
35 | @async_test
36 | async def test_issue_809_async(self):
37 | client = self.async_client
38 | buff = io.BytesIO(b"here is my data but not sure what is wrong.......")
39 | buff.seek(0)
40 | upload = await client.files_upload(file=buff)
41 | self.assertIsNotNone(upload)
42 |
--------------------------------------------------------------------------------
/integration_tests/web/test_team.py:
--------------------------------------------------------------------------------
1 | import os
2 | import unittest
3 |
4 | from integration_tests.env_variable_names import SLACK_SDK_TEST_BOT_TOKEN
5 | from slack_sdk.web import WebClient
6 |
7 |
8 | class TestWebClient(unittest.TestCase):
9 | def setUp(self):
10 | self.bot_token = os.environ[SLACK_SDK_TEST_BOT_TOKEN]
11 | self.client: WebClient = WebClient(token=self.bot_token)
12 |
13 | def tearDown(self):
14 | pass
15 |
16 | def test_team_billing_info(self):
17 | response = self.client.team_billing_info()
18 | self.assertIsNone(response.get("error"))
19 | self.assertIsNotNone(response.get("plan"))
20 |
21 | def test_team_preferences_list(self):
22 | response = self.client.team_preferences_list()
23 | self.assertIsNone(response.get("error"))
24 | self.assertIsNotNone(response.get("msg_edit_window_mins"))
25 | self.assertIsNotNone(response.get("allow_message_deletion"))
26 | self.assertIsNotNone(response.get("display_real_names"))
27 | self.assertIsNotNone(response.get("disable_file_uploads"))
28 | self.assertIsNotNone(response.get("who_can_post_general"))
29 |
--------------------------------------------------------------------------------
/logs/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/python-slack-sdk/443ca03e425d93f47822a4de5776ff0595f5ed3d/logs/.gitkeep
--------------------------------------------------------------------------------
/requirements/documentation.txt:
--------------------------------------------------------------------------------
1 | docutils==0.21.2
2 | pdoc3==0.11.6
3 |
--------------------------------------------------------------------------------
/requirements/optional.txt:
--------------------------------------------------------------------------------
1 | # pip install -r requirements/optional.txt
2 | # async modules depend on aiohttp
3 | aiodns>1.0
4 | # We recommend using 3.7.1+ for RTMClient
5 | # https://github.com/slackapi/python-slack-sdk/issues/912
6 | aiohttp>=3.7.3,<4
7 | # used only under slack_sdk/*_store
8 | boto3<=2
9 | # InstallationStore/OAuthStateStore
10 | # Since v3.20, we no longer support SQLAlchemy 1.3 or older.
11 | # If you need to use a legacy version, please add our v3.19.5 code to your project.
12 | SQLAlchemy>=1.4,<3
13 | # Socket Mode
14 | # websockets 9 is not compatible with Python 3.10
15 | websockets>=9.1,<16
16 | websocket-client>=1,<2
17 |
--------------------------------------------------------------------------------
/requirements/testing.txt:
--------------------------------------------------------------------------------
1 | # pip install -r requirements/testing.txt
2 | aiohttp<4 # used for a WebSocket server mock
3 | pytest>=7.0.1,<9
4 | pytest-asyncio<1 # for async
5 | pytest-cov>=2,<7
6 | # while flake8 5.x have issues with Python 3.12, flake8 6.x requires Python >= 3.8.1,
7 | # so 5.x should be kept in order to stay compatible with Python 3.6/3.7
8 | flake8>=5.0.4,<8
9 | # Don't change this version without running CI builds;
10 | # The latest version may not be available for older Python runtime
11 | black>=22.8.0; python_version=="3.6"
12 | black==22.10.0; python_version>"3.6"
13 | click==8.0.4 # black is affected by https://github.com/pallets/click/issues/2225
14 | psutil>=6.0.0,<8
15 | # used only under slack_sdk/*_store
16 | boto3<=2
17 | # For AWS tests
18 | moto>=4.0.13,<6
19 | mypy<=1.15.0
20 | # For AsyncSQLAlchemy tests
21 | greenlet<=4
22 | aiosqlite<=1
--------------------------------------------------------------------------------
/scripts/build_pypi_package.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | script_dir=`dirname $0`
4 | cd ${script_dir}/..
5 | rm -rf ./slack_sdk.egg-info
6 |
7 | pip install -U pip && \
8 | pip install twine build && \
9 | rm -rf dist/ build/ slack_sdk.egg-info/ && \
10 | python -m build --sdist --wheel && \
11 | twine check dist/*
12 |
--------------------------------------------------------------------------------
/scripts/deploy_to_prod_pypi_org.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | script_dir=`dirname $0`
4 | cd ${script_dir}/..
5 | rm -rf ./slack_sdk.egg-info
6 |
7 | pip install -U pip && \
8 | pip install twine build && \
9 | rm -rf dist/ build/ slack_sdk.egg-info/ && \
10 | python -m build --sdist --wheel && \
11 | twine check dist/* && \
12 | twine upload dist/*
13 |
--------------------------------------------------------------------------------
/scripts/deploy_to_test_pypi_org.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | script_dir=`dirname $0`
4 | cd ${script_dir}/..
5 | rm -rf ./slack_sdk.egg-info
6 |
7 | pip install -U pip && \
8 | pip install twine build && \
9 | rm -rf dist/ build/ slack_sdk.egg-info/ && \
10 | python -m build --sdist --wheel && \
11 | twine check dist/* && \
12 | twine upload --repository testpypi dist/*
13 |
--------------------------------------------------------------------------------
/scripts/generate_api_docs.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # Generate API documents from the latest source code
3 |
4 | script_dir=`dirname $0`
5 | cd ${script_dir}/..
6 |
7 | pip install -U -r requirements/documentation.txt
8 | pip install -U -r requirements/optional.txt
9 | rm -rf docs/static/api-docs
10 | pdoc slack_sdk --html -o docs/static/api-docs
11 | open docs/static/api-docs/slack_sdk/index.html
12 |
--------------------------------------------------------------------------------
/scripts/run_integration_tests.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # Run all the tests or a single test
3 | # all: ./scripts/run_integration_tests.sh
4 | # single: ./scripts/run_integration_tests.sh integration_tests/web/test_async_web_client.py
5 |
6 | set -e
7 |
8 | script_dir=`dirname $0`
9 | cd ${script_dir}/..
10 |
11 | pip install -U pip
12 | pip install -r requirements/testing.txt \
13 | -r requirements/optional.txt
14 |
15 | echo "Generating code ..." && python scripts/codegen.py --path .
16 | echo "Running black (code formatter) ..." && black slack_sdk/
17 |
18 | test_target="${1:-tests/integration_tests/}"
19 | PYTHONPATH=$PWD:$PYTHONPATH pytest $test_target
20 |
--------------------------------------------------------------------------------
/scripts/run_mypy.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # ./scripts/run_mypy.sh
3 |
4 | set -e
5 |
6 | script_dir=$(dirname $0)
7 | cd ${script_dir}/..
8 |
9 | pip install -U pip setuptools wheel
10 | pip install -r requirements/testing.txt \
11 | -r requirements/optional.txt
12 |
13 | mypy --config-file pyproject.toml
14 |
--------------------------------------------------------------------------------
/scripts/run_unit_tests.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # Run all the tests or a single test
3 | # all: ./scripts/run_unit_tests.sh
4 | # single: ./scripts/run_unit_tests.sh tests/slack_sdk_async/web/test_web_client_coverage.py
5 |
6 | set -e
7 |
8 | script_dir=`dirname $0`
9 | cd ${script_dir}/..
10 |
11 | pip install -U pip
12 | pip install -r requirements/testing.txt \
13 | -r requirements/optional.txt
14 |
15 | echo "Generating code ..." && python scripts/codegen.py --path .
16 | echo "Running black (code formatter) ..." && black slack_sdk/
17 |
18 | test_target="${1:-tests/}"
19 | PYTHONPATH=$PWD:$PYTHONPATH pytest $test_target
20 |
--------------------------------------------------------------------------------
/scripts/run_validation.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # all: ./scripts/run_validation.sh
3 | # single: ./scripts/run_validation.sh tests/slack_sdk_async/web/test_web_client_coverage.py
4 |
5 | set -e
6 |
7 | script_dir=`dirname $0`
8 | cd ${script_dir}/..
9 |
10 | pip install -U pip setuptools wheel
11 | pip install -r requirements/testing.txt \
12 | -r requirements/optional.txt
13 |
14 | echo "Generating code ..." && python scripts/codegen.py --path .
15 | echo "Running black (code formatter) ..." && black slack_sdk/
16 |
17 | black --check slack/ slack_sdk/ tests/ integration_tests/
18 | flake8 slack/ slack_sdk/
19 |
20 | test_target="${1:-tests/}"
21 | PYTHONPATH=$PWD:$PYTHONPATH pytest --cov-report=xml --cov=slack_sdk/ $test_target
22 |
--------------------------------------------------------------------------------
/scripts/uninstall_all.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | pip uninstall -y slack-sdk && \
4 | pip freeze | grep -v "^-e" | sed 's/@.*//' | sed 's/\=\=.*//' | xargs pip uninstall -y
5 |
--------------------------------------------------------------------------------
/setup.cfg:
--------------------------------------------------------------------------------
1 | ; Legacy package configuration, prefer pyproject.toml over setup.cfg or setup.py
2 | [metadata]
3 | url=https://github.com/slackapi/python-slack-sdk
4 | author=Slack Technologies, LLC
5 | author_email=opensource@slack.com
6 |
--------------------------------------------------------------------------------
/slack/__init__.py:
--------------------------------------------------------------------------------
1 | import logging
2 | from logging import NullHandler
3 | from slack import deprecation
4 |
5 | deprecation.show_message(__name__, "slack_sdk.web/webhook/rtm")
6 |
7 | from slack_sdk.rtm import RTMClient # noqa
8 | from slack_sdk.web.async_client import AsyncWebClient # noqa
9 | from slack_sdk.web.legacy_client import LegacyWebClient as WebClient # noqa
10 | from slack_sdk.webhook.async_client import AsyncWebhookClient # noqa
11 | from slack_sdk.webhook.client import WebhookClient # noqa
12 |
13 | # Set default logging handler to avoid "No handler found" warnings.
14 | logging.getLogger(__name__).addHandler(NullHandler())
15 |
--------------------------------------------------------------------------------
/slack/deprecation.py:
--------------------------------------------------------------------------------
1 | import os
2 | import warnings
3 |
4 |
5 | def show_message(old: str, new: str) -> None:
6 | skip_deprecation = os.environ.get("SLACKCLIENT_SKIP_DEPRECATION") # for unit tests etc.
7 | if skip_deprecation:
8 | return
9 |
10 | message = (
11 | f"{old} package is deprecated. Please use {new} package instead. "
12 | "For more info, go to https://slack.dev/python-slack-sdk/v3-migration/"
13 | )
14 | warnings.warn(message)
15 |
--------------------------------------------------------------------------------
/slack/errors.py:
--------------------------------------------------------------------------------
1 | from slack_sdk.errors import BotUserAccessError # noqa
2 | from slack_sdk.errors import SlackApiError # noqa
3 | from slack_sdk.errors import SlackClientError # noqa
4 | from slack_sdk.errors import SlackClientNotConnectedError # noqa
5 | from slack_sdk.errors import SlackObjectFormationError # noqa
6 | from slack_sdk.errors import SlackRequestError # noqa
7 |
8 | from slack import deprecation
9 |
10 | deprecation.show_message(__name__, "slack_sdk.errors")
11 |
--------------------------------------------------------------------------------
/slack/py.typed:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/python-slack-sdk/443ca03e425d93f47822a4de5776ff0595f5ed3d/slack/py.typed
--------------------------------------------------------------------------------
/slack/rtm/__init__.py:
--------------------------------------------------------------------------------
1 | from slack_sdk.rtm import RTMClient # noqa
2 | from slack_sdk.web.legacy_client import LegacyWebClient as WebClient # noqa
3 |
4 | from slack import deprecation
5 |
6 | deprecation.show_message(__name__, "slack_sdk.web/rtm")
7 |
--------------------------------------------------------------------------------
/slack/rtm/client.py:
--------------------------------------------------------------------------------
1 | from slack_sdk.rtm import RTMClient # noqa
2 | from slack_sdk.web.legacy_client import LegacyWebClient as WebClient # noqa
3 |
4 | from slack import deprecation
5 |
6 | deprecation.show_message(__name__, "slack_sdk.rtm.client")
7 |
--------------------------------------------------------------------------------
/slack/signature/__init__.py:
--------------------------------------------------------------------------------
1 | from slack_sdk.signature import SignatureVerifier # noqa
2 |
3 | from slack import deprecation
4 |
5 | deprecation.show_message(__name__, "slack_sdk.signature")
6 |
--------------------------------------------------------------------------------
/slack/version.py:
--------------------------------------------------------------------------------
1 | from slack_sdk.version import __version__ # noqa
2 |
--------------------------------------------------------------------------------
/slack/web/__init__.py:
--------------------------------------------------------------------------------
1 | import slack_sdk.version as slack_version # noqa
2 | from slack import deprecation
3 | from slack_sdk.web.async_client import AsyncSlackResponse # noqa
4 | from slack_sdk.web.async_client import AsyncWebClient # noqa
5 | from slack_sdk.web.internal_utils import _to_0_or_1_if_bool # noqa
6 | from slack_sdk.web.internal_utils import convert_bool_to_0_or_1 # noqa
7 | from slack_sdk.web.internal_utils import get_user_agent # noqa
8 | from slack_sdk.web.legacy_client import LegacyWebClient as WebClient # noqa
9 | from slack_sdk.web.slack_response import SlackResponse # noqa
10 |
11 | deprecation.show_message(__name__, "slack_sdk.web")
12 |
--------------------------------------------------------------------------------
/slack/web/async_client.py:
--------------------------------------------------------------------------------
1 | # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2 | #
3 | # *** DO NOT EDIT THIS FILE ***
4 | #
5 | # 1) Modify slack/web/client.py
6 | # 2) Run `python scripts/codegen.py`
7 | # 3) Run `black slack_sdk/`
8 | #
9 | # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
10 |
11 | from slack import deprecation
12 | from slack_sdk.web.legacy_client import LegacyWebClient as WebClient # noqa
13 | from slack_sdk.web.async_client import AsyncWebClient # noqa
14 | from slack_sdk.web.async_client import AsyncSlackResponse # noqa
15 |
16 | deprecation.show_message(__name__, "slack_sdk.web.client")
17 |
--------------------------------------------------------------------------------
/slack/web/classes/__init__.py:
--------------------------------------------------------------------------------
1 | from slack_sdk.models import BaseObject # noqa
2 | from slack_sdk.models import JsonObject # noqa
3 | from slack_sdk.models import JsonValidator # noqa
4 | from slack_sdk.models import EnumValidator # noqa
5 | from slack_sdk.models import extract_json # noqa
6 | from slack_sdk.models import show_unknown_key_warning # noqa
7 |
8 | from slack import deprecation
9 |
10 | deprecation.show_message(__name__, "slack_sdk.models")
11 |
--------------------------------------------------------------------------------
/slack/web/classes/actions.py:
--------------------------------------------------------------------------------
1 | from slack_sdk.models.attachments import AbstractActionSelector # noqa
2 | from slack_sdk.models.attachments import Action # noqa
3 | from slack_sdk.models.attachments import ActionButton # noqa
4 | from slack_sdk.models.attachments import ActionChannelSelector # noqa
5 | from slack_sdk.models.attachments import ActionConversationSelector # noqa
6 | from slack_sdk.models.attachments import ActionExternalSelector # noqa
7 | from slack_sdk.models.attachments import ActionLinkButton # noqa
8 | from slack_sdk.models.attachments import ActionUserSelector # noqa
9 | from slack_sdk.models.dialogs import ActionStaticSelector # noqa
10 |
11 | from slack import deprecation
12 |
13 | deprecation.show_message(__name__, "slack_sdk.models.attachments/dialogs")
14 |
--------------------------------------------------------------------------------
/slack/web/classes/attachments.py:
--------------------------------------------------------------------------------
1 | from slack_sdk.models.attachments import Attachment # noqa
2 | from slack_sdk.models.attachments import AttachmentField # noqa
3 | from slack_sdk.models.attachments import BlockAttachment # noqa
4 | from slack_sdk.models.attachments import InteractiveAttachment # noqa
5 | from slack_sdk.models.attachments import SeededColors # noqa
6 |
7 | from slack import deprecation
8 |
9 | deprecation.show_message(__name__, "slack_sdk.models.attachments")
10 |
--------------------------------------------------------------------------------
/slack/web/classes/blocks.py:
--------------------------------------------------------------------------------
1 | from slack import deprecation
2 | from slack_sdk.models.blocks import ActionsBlock # noqa
3 | from slack_sdk.models.blocks import Block # noqa
4 | from slack_sdk.models.blocks import CallBlock # noqa
5 | from slack_sdk.models.blocks import ContextBlock # noqa
6 | from slack_sdk.models.blocks import DividerBlock # noqa
7 | from slack_sdk.models.blocks import FileBlock # noqa
8 | from slack_sdk.models.blocks import HeaderBlock # noqa
9 | from slack_sdk.models.blocks import ImageBlock # noqa
10 | from slack_sdk.models.blocks import InputBlock # noqa
11 | from slack_sdk.models.blocks import SectionBlock # noqa
12 |
13 | deprecation.show_message(__name__, "slack_sdk.models.blocks")
14 |
--------------------------------------------------------------------------------
/slack/web/classes/dialog_elements.py:
--------------------------------------------------------------------------------
1 | from slack_sdk.models.dialogs import AbstractDialogSelector # noqa
2 | from slack_sdk.models.dialogs import DialogChannelSelector # noqa
3 | from slack_sdk.models.dialogs import DialogConversationSelector # noqa
4 | from slack_sdk.models.dialogs import DialogExternalSelector # noqa
5 | from slack_sdk.models.dialogs import DialogStaticSelector # noqa
6 | from slack_sdk.models.dialogs import DialogTextArea # noqa
7 | from slack_sdk.models.dialogs import DialogTextComponent # noqa
8 | from slack_sdk.models.dialogs import DialogTextField # noqa
9 | from slack_sdk.models.dialogs import DialogUserSelector # noqa
10 | from slack_sdk.models.dialogs import TextElementSubtypes # noqa
11 |
12 | from slack import deprecation
13 |
14 | deprecation.show_message(__name__, "slack_sdk.models.blocks")
15 |
--------------------------------------------------------------------------------
/slack/web/classes/dialogs.py:
--------------------------------------------------------------------------------
1 | from slack_sdk.models.dialogs import DialogBuilder # noqa
2 |
3 | from slack import deprecation
4 |
5 | deprecation.show_message(__name__, "slack_sdk.models.dialogs")
6 |
--------------------------------------------------------------------------------
/slack/web/classes/messages.py:
--------------------------------------------------------------------------------
1 | from slack_sdk.models.messages.message import Message # noqa
2 |
--------------------------------------------------------------------------------
/slack/web/classes/objects.py:
--------------------------------------------------------------------------------
1 | from slack_sdk.models.blocks import ButtonStyles # noqa
2 | from slack_sdk.models.blocks import ConfirmObject # noqa
3 | from slack_sdk.models.blocks import DynamicSelectElementTypes # noqa
4 | from slack_sdk.models.blocks import MarkdownTextObject # noqa
5 | from slack_sdk.models.blocks import Option # noqa
6 | from slack_sdk.models.blocks import OptionGroup # noqa
7 | from slack_sdk.models.blocks import PlainTextObject # noqa
8 | from slack_sdk.models.blocks import TextObject # noqa
9 | from slack_sdk.models.messages import ChannelLink # noqa
10 | from slack_sdk.models.messages import DateLink # noqa
11 | from slack_sdk.models.messages import EveryoneLink # noqa
12 | from slack_sdk.models.messages import HereLink # noqa
13 | from slack_sdk.models.messages import Link # noqa
14 | from slack_sdk.models.messages import ObjectLink # noqa
15 |
16 |
17 | from slack import deprecation
18 |
19 | deprecation.show_message(__name__, "slack_sdk.models.blocks/messages")
20 |
--------------------------------------------------------------------------------
/slack/web/classes/views.py:
--------------------------------------------------------------------------------
1 | from slack_sdk.models.views import View # noqa
2 | from slack_sdk.models.views import ViewState # noqa
3 | from slack_sdk.models.views import ViewStateValue # noqa
4 |
5 | from slack import deprecation
6 |
7 | deprecation.show_message(__name__, "slack_sdk.models.views")
8 |
--------------------------------------------------------------------------------
/slack/web/client.py:
--------------------------------------------------------------------------------
1 | from slack import deprecation
2 | from slack_sdk.web.legacy_client import LegacyWebClient as WebClient # noqa
3 |
4 | deprecation.show_message(__name__, "slack_sdk.web.client")
5 |
--------------------------------------------------------------------------------
/slack/web/deprecation.py:
--------------------------------------------------------------------------------
1 | import os
2 | import warnings
3 |
4 | # https://api.slack.com/changelog/2020-01-deprecating-antecedents-to-the-conversations-api
5 | deprecated_method_prefixes_2020_01 = [
6 | "channels.",
7 | "groups.",
8 | "im.",
9 | "mpim.",
10 | "admin.conversations.whitelist.",
11 | ]
12 |
13 |
14 | def show_2020_01_deprecation(method_name: str):
15 | """Prints a warning if the given method is deprecated"""
16 |
17 | skip_deprecation = os.environ.get("SLACKCLIENT_SKIP_DEPRECATION") # for unit tests etc.
18 | if skip_deprecation:
19 | return
20 | if not method_name:
21 | return
22 |
23 | matched_prefixes = [prefix for prefix in deprecated_method_prefixes_2020_01 if method_name.startswith(prefix)]
24 | if len(matched_prefixes) > 0:
25 | message = (
26 | f"{method_name} is deprecated. Please use the Conversations API instead. "
27 | "For more info, go to "
28 | "https://api.slack.com/changelog/2020-01-deprecating-antecedents-to-the-conversations-api"
29 | )
30 | warnings.warn(message)
31 |
--------------------------------------------------------------------------------
/slack/web/slack_response.py:
--------------------------------------------------------------------------------
1 | from slack import deprecation
2 | from slack_sdk.web.legacy_slack_response import ( # noqa
3 | LegacySlackResponse as SlackResponse,
4 | )
5 |
6 | deprecation.show_message(__name__, "slack_sdk.web.slack_response")
7 |
--------------------------------------------------------------------------------
/slack/webhook/__init__.py:
--------------------------------------------------------------------------------
1 | from slack_sdk.webhook.webhook_response import WebhookResponse # noqa
2 | from slack_sdk.webhook.client import WebhookClient # noqa
3 | from slack_sdk.webhook.async_client import AsyncWebhookClient # noqa
4 |
5 | from slack import deprecation
6 |
7 | deprecation.show_message(__name__, "slack_sdk.webhook")
8 |
--------------------------------------------------------------------------------
/slack/webhook/internal_utils.py:
--------------------------------------------------------------------------------
1 | import logging
2 | from typing import Optional, Dict
3 |
4 | from slack.web import get_user_agent, convert_bool_to_0_or_1
5 | from slack.web.internal_utils import _parse_web_class_objects
6 | from slack.webhook import WebhookResponse
7 |
8 |
9 | def _build_body(original_body: Dict[str, any]) -> Dict[str, any]:
10 | body = {k: v for k, v in original_body.items() if v is not None}
11 | body = convert_bool_to_0_or_1(body)
12 | _parse_web_class_objects(body)
13 | return body
14 |
15 |
16 | def _build_request_headers(
17 | default_headers: Dict[str, str],
18 | additional_headers: Optional[Dict[str, str]],
19 | ) -> Dict[str, str]:
20 | if additional_headers is None:
21 | return {}
22 |
23 | request_headers = {
24 | "User-Agent": get_user_agent(),
25 | "Content-Type": "application/json;charset=utf-8",
26 | }
27 | request_headers.update(default_headers)
28 | if additional_headers:
29 | request_headers.update(additional_headers)
30 | return request_headers
31 |
32 |
33 | def _debug_log_response(logger, resp: WebhookResponse) -> None:
34 | if logger.level <= logging.DEBUG:
35 | logger.debug(
36 | "Received the following response - "
37 | f"status: {resp.status_code}, "
38 | f"headers: {(dict(resp.headers))}, "
39 | f"body: {resp.body}"
40 | )
41 |
--------------------------------------------------------------------------------
/slack/webhook/webhook_response.py:
--------------------------------------------------------------------------------
1 | class WebhookResponse:
2 | def __init__(
3 | self,
4 | *,
5 | url: str,
6 | status_code: int,
7 | body: str,
8 | headers: dict,
9 | ):
10 | self.api_url = url
11 | self.status_code = status_code
12 | self.body = body
13 | self.headers = headers
14 |
--------------------------------------------------------------------------------
/slack_sdk/aiohttp_version_checker.py:
--------------------------------------------------------------------------------
1 | """Internal module for checking aiohttp compatibility of async modules"""
2 | import logging
3 | from typing import Callable
4 |
5 |
6 | def _print_warning_log(message: str) -> None:
7 | logging.getLogger(__name__).warning(message)
8 |
9 |
10 | def validate_aiohttp_version(
11 | aiohttp_version: str,
12 | print_warning: Callable[[str], None] = _print_warning_log,
13 | ):
14 | if aiohttp_version is not None:
15 | elements = aiohttp_version.split(".")
16 | if len(elements) >= 3:
17 | # patch version can be a non-numeric value
18 | major, minor, patch = int(elements[0]), int(elements[1]), elements[2]
19 | if major <= 2 or (major == 3 and (minor == 6 or (minor == 7 and patch == "0"))):
20 | print_warning(
21 | "We highly recommend upgrading aiohttp to 3.7.3 or higher versions."
22 | "An older version of the library may not work with the Slack server-side in the future."
23 | )
24 |
--------------------------------------------------------------------------------
/slack_sdk/audit_logs/__init__.py:
--------------------------------------------------------------------------------
1 | """Audit Logs API is a set of APIs for monitoring what’s happening in your Enterprise Grid organization.
2 |
3 | Refer to https://slack.dev/python-slack-sdk/audit-logs/ for details.
4 | """
5 | from .v1.client import AuditLogsClient
6 | from .v1.response import AuditLogsResponse
7 |
8 | __all__ = [
9 | "AuditLogsClient",
10 | "AuditLogsResponse",
11 | ]
12 |
--------------------------------------------------------------------------------
/slack_sdk/audit_logs/async_client.py:
--------------------------------------------------------------------------------
1 | from .v1.async_client import AsyncAuditLogsClient
2 |
3 | __all__ = [
4 | "AsyncAuditLogsClient",
5 | ]
6 |
--------------------------------------------------------------------------------
/slack_sdk/audit_logs/v1/__init__.py:
--------------------------------------------------------------------------------
1 | """Audit Logs API is a set of APIs for monitoring what’s happening in your Enterprise Grid organization.
2 |
3 | Refer to https://slack.dev/python-slack-sdk/audit-logs/ for details.
4 | """
5 |
--------------------------------------------------------------------------------
/slack_sdk/audit_logs/v1/internal_utils.py:
--------------------------------------------------------------------------------
1 | import logging
2 | from typing import Optional, Dict, Any
3 | from urllib.parse import quote
4 |
5 | from slack_sdk.web.internal_utils import get_user_agent
6 | from .response import AuditLogsResponse
7 |
8 |
9 | def _build_query(params: Optional[Dict[str, Any]]) -> str:
10 | if params is not None and len(params) > 0:
11 | return "&".join({f"{quote(str(k))}={quote(str(v))}" for k, v in params.items() if v is not None})
12 | return ""
13 |
14 |
15 | def _build_request_headers(
16 | token: str,
17 | default_headers: Dict[str, str],
18 | additional_headers: Optional[Dict[str, str]],
19 | ) -> Dict[str, str]:
20 | request_headers = {
21 | "Content-Type": "application/json;charset=utf-8",
22 | "Authorization": f"Bearer {token}",
23 | }
24 | if default_headers is None or "User-Agent" not in default_headers:
25 | request_headers["User-Agent"] = get_user_agent()
26 | if default_headers is not None:
27 | request_headers.update(default_headers)
28 | if additional_headers is not None:
29 | request_headers.update(additional_headers)
30 | return request_headers
31 |
32 |
33 | def _debug_log_response(logger, resp: AuditLogsResponse) -> None:
34 | if logger.level <= logging.DEBUG:
35 | logger.debug(
36 | "Received the following response - "
37 | f"status: {resp.status_code}, "
38 | f"headers: {(dict(resp.headers))}, "
39 | f"body: {resp.raw_body}"
40 | )
41 |
--------------------------------------------------------------------------------
/slack_sdk/audit_logs/v1/response.py:
--------------------------------------------------------------------------------
1 | import json
2 | from typing import Dict, Any, Optional
3 |
4 | from slack_sdk.audit_logs.v1.logs import LogsResponse
5 |
6 |
7 | # TODO: Unlike WebClient's responses, this class has not yet provided __iter__ method
8 | class AuditLogsResponse:
9 | url: str
10 | status_code: int
11 | headers: Dict[str, Any]
12 | raw_body: Optional[str]
13 | body: Optional[Dict[str, Any]]
14 | typed_body: Optional[LogsResponse]
15 |
16 | @property # type: ignore[no-redef]
17 | def typed_body(self) -> Optional[LogsResponse]:
18 | if self.body is None:
19 | return None
20 | return LogsResponse(**self.body)
21 |
22 | def __init__(
23 | self,
24 | *,
25 | url: str,
26 | status_code: int,
27 | raw_body: Optional[str],
28 | headers: dict,
29 | ):
30 | self.url = url
31 | self.status_code = status_code
32 | self.headers = headers
33 | self.raw_body = raw_body
34 | self.body = json.loads(raw_body) if raw_body is not None and raw_body.startswith("{") else None
35 |
--------------------------------------------------------------------------------
/slack_sdk/http_retry/__init__.py:
--------------------------------------------------------------------------------
1 | from typing import List
2 |
3 | from .handler import RetryHandler
4 | from .builtin_handlers import (
5 | ConnectionErrorRetryHandler,
6 | RateLimitErrorRetryHandler,
7 | )
8 | from .interval_calculator import RetryIntervalCalculator
9 | from .builtin_interval_calculators import (
10 | FixedValueRetryIntervalCalculator,
11 | BackoffRetryIntervalCalculator,
12 | )
13 | from .jitter import Jitter
14 | from .request import HttpRequest
15 | from .response import HttpResponse
16 | from .state import RetryState
17 |
18 | connect_error_retry_handler = ConnectionErrorRetryHandler()
19 | rate_limit_error_retry_handler = RateLimitErrorRetryHandler()
20 |
21 |
22 | def default_retry_handlers() -> List[RetryHandler]:
23 | return [connect_error_retry_handler]
24 |
25 |
26 | def all_builtin_retry_handlers() -> List[RetryHandler]:
27 | return [
28 | connect_error_retry_handler,
29 | rate_limit_error_retry_handler,
30 | ]
31 |
32 |
33 | __all__ = [
34 | "RetryHandler",
35 | "ConnectionErrorRetryHandler",
36 | "RateLimitErrorRetryHandler",
37 | "RetryIntervalCalculator",
38 | "FixedValueRetryIntervalCalculator",
39 | "BackoffRetryIntervalCalculator",
40 | "Jitter",
41 | "HttpRequest",
42 | "HttpResponse",
43 | "RetryState",
44 | "connect_error_retry_handler",
45 | "rate_limit_error_retry_handler",
46 | "default_retry_handlers",
47 | "all_builtin_retry_handlers",
48 | ]
49 |
--------------------------------------------------------------------------------
/slack_sdk/http_retry/interval_calculator.py:
--------------------------------------------------------------------------------
1 | class RetryIntervalCalculator:
2 | """Retry interval calculator interface."""
3 |
4 | def calculate_sleep_duration(self, current_attempt: int) -> float:
5 | """Calculates an interval duration in seconds.
6 |
7 | Args:
8 | current_attempt: the number of the current attempt (zero-origin; 0 means no retries are done so far)
9 | Returns:
10 | calculated interval duration in seconds
11 | """
12 | raise NotImplementedError()
13 |
--------------------------------------------------------------------------------
/slack_sdk/http_retry/jitter.py:
--------------------------------------------------------------------------------
1 | import random
2 |
3 |
4 | class Jitter:
5 | """Jitter interface"""
6 |
7 | def recalculate(self, duration: float) -> float:
8 | """Recalculate the given duration.
9 | see also: https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/
10 |
11 | Args:
12 | duration: the duration in seconds
13 |
14 | Returns:
15 | A new duration that the jitter amount is added
16 | """
17 | raise NotImplementedError()
18 |
19 |
20 | class RandomJitter(Jitter):
21 | """Random jitter implementation"""
22 |
23 | def recalculate(self, duration: float) -> float:
24 | return duration + random.random()
25 |
--------------------------------------------------------------------------------
/slack_sdk/http_retry/request.py:
--------------------------------------------------------------------------------
1 | from typing import Dict, Optional, List, Union, Any
2 | from urllib.request import Request
3 |
4 |
5 | class HttpRequest:
6 | """HTTP request representation"""
7 |
8 | method: str
9 | url: str
10 | headers: Dict[str, Union[str, List[str]]]
11 | body_params: Optional[Dict[str, Any]]
12 | data: Optional[bytes]
13 |
14 | def __init__(
15 | self,
16 | *,
17 | method: str,
18 | url: str,
19 | headers: Dict[str, Union[str, List[str]]],
20 | body_params: Optional[Dict[str, Any]] = None,
21 | data: Optional[bytes] = None,
22 | ):
23 | self.method = method
24 | self.url = url
25 | self.headers = {k: v if isinstance(v, list) else [v] for k, v in headers.items()}
26 | self.body_params = body_params
27 | self.data = data
28 |
29 | @classmethod
30 | def from_urllib_http_request(cls, req: Request) -> "HttpRequest":
31 | return HttpRequest(
32 | method=req.method, # type: ignore[arg-type]
33 | url=req.full_url,
34 | headers={k: v if isinstance(v, list) else [v] for k, v in req.headers.items()},
35 | data=req.data, # type: ignore[arg-type]
36 | )
37 |
--------------------------------------------------------------------------------
/slack_sdk/http_retry/response.py:
--------------------------------------------------------------------------------
1 | from typing import Dict, Optional, List, Union, Any
2 |
3 |
4 | class HttpResponse:
5 | """HTTP response representation"""
6 |
7 | status_code: int
8 | headers: Dict[str, Union[List[str], str]]
9 | body: Optional[Dict[str, Any]]
10 | data: Optional[bytes]
11 |
12 | def __init__(
13 | self,
14 | *,
15 | status_code: Union[int, str],
16 | headers: Dict[str, Union[str, List[str]]],
17 | body: Optional[Dict[str, Any]] = None,
18 | data: Optional[bytes] = None,
19 | ):
20 | self.status_code = int(status_code)
21 | self.headers = {k: v if isinstance(v, list) else [v] for k, v in headers.items()}
22 | self.body = body
23 | self.data = data
24 |
--------------------------------------------------------------------------------
/slack_sdk/http_retry/state.py:
--------------------------------------------------------------------------------
1 | from typing import Optional, Any, Dict
2 |
3 |
4 | class RetryState:
5 | next_attempt_requested: bool
6 | current_attempt: int # zero-origin
7 | custom_values: Optional[Dict[str, Any]]
8 |
9 | def __init__(
10 | self,
11 | *,
12 | current_attempt: int = 0,
13 | custom_values: Optional[Dict[str, Any]] = None,
14 | ):
15 | self.next_attempt_requested = False
16 | self.current_attempt = current_attempt
17 | self.custom_values = custom_values
18 |
19 | def increment_current_attempt(self) -> int:
20 | self.current_attempt += 1
21 | return self.current_attempt
22 |
--------------------------------------------------------------------------------
/slack_sdk/models/dialoags.py:
--------------------------------------------------------------------------------
1 | from slack_sdk.models.dialogs import AbstractDialogSelector
2 | from slack_sdk.models.dialogs import DialogChannelSelector
3 | from slack_sdk.models.dialogs import DialogConversationSelector
4 | from slack_sdk.models.dialogs import DialogExternalSelector
5 | from slack_sdk.models.dialogs import DialogStaticSelector
6 | from slack_sdk.models.dialogs import DialogTextArea
7 | from slack_sdk.models.dialogs import DialogTextComponent
8 | from slack_sdk.models.dialogs import DialogTextField
9 | from slack_sdk.models.dialogs import DialogUserSelector
10 | from slack_sdk.models.dialogs import TextElementSubtypes
11 | from slack_sdk.models.dialogs import DialogBuilder
12 |
13 | from slack import deprecation
14 |
15 | deprecation.show_message(__name__, "slack_sdk.models.dialogs")
16 |
17 | __all__ = [
18 | "AbstractDialogSelector",
19 | "DialogChannelSelector",
20 | "DialogConversationSelector",
21 | "DialogExternalSelector",
22 | "DialogStaticSelector",
23 | "DialogTextArea",
24 | "DialogTextComponent",
25 | "DialogTextField",
26 | "DialogUserSelector",
27 | "TextElementSubtypes",
28 | "DialogBuilder",
29 | ]
30 |
--------------------------------------------------------------------------------
/slack_sdk/models/metadata/__init__.py:
--------------------------------------------------------------------------------
1 | from typing import Dict, Any
2 | from slack_sdk.models.basic_objects import JsonObject
3 |
4 |
5 | class Metadata(JsonObject):
6 | """Message metadata
7 |
8 | https://api.slack.com/metadata
9 | """
10 |
11 | attributes = {
12 | "event_type",
13 | "event_payload",
14 | }
15 |
16 | def __init__(
17 | self,
18 | event_type: str,
19 | event_payload: Dict[str, Any],
20 | **kwargs,
21 | ):
22 | self.event_type = event_type
23 | self.event_payload = event_payload
24 | self.additional_attributes = kwargs
25 |
26 | def __str__(self):
27 | return str(self.get_non_null_attributes())
28 |
29 | def __repr__(self):
30 | return self.__str__()
31 |
--------------------------------------------------------------------------------
/slack_sdk/oauth/__init__.py:
--------------------------------------------------------------------------------
1 | """Modules for implementing the Slack OAuth flow
2 |
3 | https://slack.dev/python-slack-sdk/oauth/
4 | """
5 | from .authorize_url_generator import AuthorizeUrlGenerator
6 | from .authorize_url_generator import OpenIDConnectAuthorizeUrlGenerator
7 | from .installation_store import InstallationStore
8 | from .redirect_uri_page_renderer import RedirectUriPageRenderer
9 | from .state_store import OAuthStateStore
10 | from .state_utils import OAuthStateUtils
11 |
12 | __all__ = [
13 | "AuthorizeUrlGenerator",
14 | "OpenIDConnectAuthorizeUrlGenerator",
15 | "InstallationStore",
16 | "RedirectUriPageRenderer",
17 | "OAuthStateStore",
18 | "OAuthStateUtils",
19 | ]
20 |
--------------------------------------------------------------------------------
/slack_sdk/oauth/installation_store/__init__.py:
--------------------------------------------------------------------------------
1 | from .file import FileInstallationStore
2 | from .installation_store import InstallationStore
3 | from .models import Bot, Installation
4 |
5 | __all__ = [
6 | "FileInstallationStore",
7 | "InstallationStore",
8 | "Bot",
9 | "Installation",
10 | ]
11 |
--------------------------------------------------------------------------------
/slack_sdk/oauth/installation_store/models/__init__.py:
--------------------------------------------------------------------------------
1 | from .bot import Bot
2 | from .installation import Installation
3 |
4 | __all__ = [
5 | "Bot",
6 | "Installation",
7 | ]
8 |
--------------------------------------------------------------------------------
/slack_sdk/oauth/state_store/__init__.py:
--------------------------------------------------------------------------------
1 | """OAuth state parameter data store
2 |
3 | Refer to https://slack.dev/python-slack-sdk/oauth/ for details.
4 | """
5 | # from .amazon_s3_state_store import AmazonS3OAuthStateStore
6 | from .file import FileOAuthStateStore
7 | from .state_store import OAuthStateStore
8 |
9 | __all__ = [
10 | "FileOAuthStateStore",
11 | "OAuthStateStore",
12 | ]
13 |
--------------------------------------------------------------------------------
/slack_sdk/oauth/state_store/async_state_store.py:
--------------------------------------------------------------------------------
1 | from logging import Logger
2 |
3 |
4 | class AsyncOAuthStateStore:
5 | @property
6 | def logger(self) -> Logger:
7 | raise NotImplementedError()
8 |
9 | async def async_issue(self, *args, **kwargs) -> str:
10 | raise NotImplementedError()
11 |
12 | async def async_consume(self, state: str) -> bool:
13 | raise NotImplementedError()
14 |
--------------------------------------------------------------------------------
/slack_sdk/oauth/state_store/state_store.py:
--------------------------------------------------------------------------------
1 | from logging import Logger
2 |
3 |
4 | class OAuthStateStore:
5 | @property
6 | def logger(self) -> Logger:
7 | raise NotImplementedError()
8 |
9 | def issue(self, *args, **kwargs) -> str:
10 | raise NotImplementedError()
11 |
12 | def consume(self, state: str) -> bool:
13 | raise NotImplementedError()
14 |
--------------------------------------------------------------------------------
/slack_sdk/oauth/token_rotation/__init__.py:
--------------------------------------------------------------------------------
1 | from .rotator import TokenRotator
2 |
3 | __all__ = [
4 | "TokenRotator",
5 | ]
6 |
--------------------------------------------------------------------------------
/slack_sdk/proxy_env_variable_loader.py:
--------------------------------------------------------------------------------
1 | """Internal module for loading proxy-related env variables"""
2 | import logging
3 | import os
4 | from typing import Optional
5 |
6 | _default_logger = logging.getLogger(__name__)
7 |
8 |
9 | def load_http_proxy_from_env(logger: logging.Logger = _default_logger) -> Optional[str]:
10 | proxy_url = (
11 | os.environ.get("HTTPS_PROXY")
12 | or os.environ.get("https_proxy")
13 | or os.environ.get("HTTP_PROXY")
14 | or os.environ.get("http_proxy")
15 | )
16 | if proxy_url is None:
17 | return None
18 | if len(proxy_url.strip()) == 0:
19 | # If the value is an empty string, the intention should be unsetting it
20 | logger.debug("The Slack SDK ignored the proxy env variable as an empty value is set.")
21 | return None
22 |
23 | logger.debug(f"HTTP proxy URL has been loaded from an env variable: {proxy_url}")
24 | return proxy_url
25 |
--------------------------------------------------------------------------------
/slack_sdk/py.typed:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/python-slack-sdk/443ca03e425d93f47822a4de5776ff0595f5ed3d/slack_sdk/py.typed
--------------------------------------------------------------------------------
/slack_sdk/rtm/v2/__init__.py:
--------------------------------------------------------------------------------
1 | from slack_sdk.rtm_v2 import RTMClient
2 |
3 | __all__ = [
4 | "RTMClient",
5 | ]
6 |
--------------------------------------------------------------------------------
/slack_sdk/scim/__init__.py:
--------------------------------------------------------------------------------
1 | """SCIM API is a set of APIs for provisioning and managing user accounts and groups.
2 | SCIM is used by Single Sign-On (SSO) services and identity providers to manage people across a variety of tools,
3 | including Slack.
4 |
5 | Refer to https://slack.dev/python-slack-sdk/scim/ for details.
6 | """
7 | from .v1.client import SCIMClient
8 | from .v1.response import SCIMResponse
9 | from .v1.response import SearchUsersResponse, ReadUserResponse
10 | from .v1.response import SearchGroupsResponse, ReadGroupResponse
11 | from .v1.user import User
12 | from .v1.group import Group
13 |
14 | __all__ = [
15 | "SCIMClient",
16 | "SCIMResponse",
17 | "SearchUsersResponse",
18 | "ReadUserResponse",
19 | "SearchGroupsResponse",
20 | "ReadGroupResponse",
21 | "User",
22 | "Group",
23 | ]
24 |
--------------------------------------------------------------------------------
/slack_sdk/scim/async_client.py:
--------------------------------------------------------------------------------
1 | from .v1.async_client import AsyncSCIMClient
2 |
3 | __all__ = [
4 | "AsyncSCIMClient",
5 | ]
6 |
--------------------------------------------------------------------------------
/slack_sdk/scim/v1/__init__.py:
--------------------------------------------------------------------------------
1 | """SCIM API is a set of APIs for provisioning and managing user accounts and groups.
2 | SCIM is used by Single Sign-On (SSO) services and identity providers to manage people across a variety of tools,
3 | including Slack.
4 |
5 | Refer to https://slack.dev/python-slack-sdk/scim/ for details.
6 | """
7 |
--------------------------------------------------------------------------------
/slack_sdk/scim/v1/default_arg.py:
--------------------------------------------------------------------------------
1 | class DefaultArg:
2 | pass
3 |
4 |
5 | NotGiven = DefaultArg()
6 |
--------------------------------------------------------------------------------
/slack_sdk/scim/v1/types.py:
--------------------------------------------------------------------------------
1 | from typing import Optional, Union, Dict, Any
2 |
3 | from .default_arg import DefaultArg, NotGiven
4 | from .internal_utils import _to_dict_without_not_given
5 |
6 |
7 | class TypeAndValue:
8 | primary: Union[Optional[bool], DefaultArg]
9 | type: Union[Optional[str], DefaultArg]
10 | value: Union[Optional[str], DefaultArg]
11 | unknown_fields: Dict[str, Any]
12 |
13 | def __init__(
14 | self,
15 | *,
16 | primary: Union[Optional[bool], DefaultArg] = NotGiven,
17 | type: Union[Optional[str], DefaultArg] = NotGiven,
18 | value: Union[Optional[str], DefaultArg] = NotGiven,
19 | **kwargs,
20 | ) -> None:
21 | self.primary = primary
22 | self.type = type
23 | self.value = value
24 | self.unknown_fields = kwargs
25 |
26 | def to_dict(self) -> dict:
27 | return _to_dict_without_not_given(self)
28 |
--------------------------------------------------------------------------------
/slack_sdk/socket_mode/__init__.py:
--------------------------------------------------------------------------------
1 | """Socket Mode is a method of connecting your app to Slack’s APIs using WebSockets instead of HTTP.
2 | You can use slack_sdk.socket_mode.SocketModeClient for managing Socket Mode connections
3 | and performing interactions with Slack.
4 |
5 | https://api.slack.com/apis/connections/socket
6 | """
7 | from .builtin import SocketModeClient
8 |
9 | __all__ = [
10 | "SocketModeClient",
11 | ]
12 |
--------------------------------------------------------------------------------
/slack_sdk/socket_mode/async_listeners.py:
--------------------------------------------------------------------------------
1 | from typing import Optional, Callable
2 |
3 | from slack_sdk.socket_mode.request import SocketModeRequest
4 |
5 |
6 | class AsyncWebSocketMessageListener(Callable): # type: ignore[misc]
7 | async def __call__(
8 | client: "AsyncBaseSocketModeClient", # type: ignore[name-defined] # noqa: F821
9 | message: dict,
10 | raw_message: Optional[str] = None,
11 | ): # noqa: F821
12 | raise NotImplementedError()
13 |
14 |
15 | class AsyncSocketModeRequestListener(Callable): # type: ignore[misc]
16 | async def __call__(
17 | client: "AsyncBaseSocketModeClient", # type: ignore[name-defined] # noqa: F821
18 | request: SocketModeRequest,
19 | ): # noqa: F821
20 | raise NotImplementedError()
21 |
--------------------------------------------------------------------------------
/slack_sdk/socket_mode/builtin/__init__.py:
--------------------------------------------------------------------------------
1 | from .client import SocketModeClient
2 |
3 | __all__ = [
4 | "SocketModeClient",
5 | ]
6 |
--------------------------------------------------------------------------------
/slack_sdk/socket_mode/builtin/frame_header.py:
--------------------------------------------------------------------------------
1 | class FrameHeader:
2 | fin: int
3 | rsv1: int
4 | rsv2: int
5 | rsv3: int
6 | opcode: int
7 | masked: int
8 | length: int
9 |
10 | # Opcode
11 | # https://tools.ietf.org/html/rfc6455#section-5.2
12 | # Non-control frames
13 | # %x0 denotes a continuation frame
14 | OPCODE_CONTINUATION = 0x0
15 | # %x1 denotes a text frame
16 | OPCODE_TEXT = 0x1
17 | # %x2 denotes a binary frame
18 | OPCODE_BINARY = 0x2
19 | # %x3-7 are reserved for further non-control frames
20 |
21 | # Control frames
22 | # %x8 denotes a connection close
23 | OPCODE_CLOSE = 0x8
24 | # %x9 denotes a ping
25 | OPCODE_PING = 0x9
26 | # %xA denotes a pong
27 | OPCODE_PONG = 0xA
28 |
29 | # %xB-F are reserved for further control frames
30 |
31 | def __init__(
32 | self,
33 | opcode: int,
34 | fin: int = 1,
35 | rsv1: int = 0,
36 | rsv2: int = 0,
37 | rsv3: int = 0,
38 | masked: int = 0,
39 | length: int = 0,
40 | ):
41 | self.opcode = opcode
42 | self.fin = fin
43 | self.rsv1 = rsv1
44 | self.rsv2 = rsv2
45 | self.rsv3 = rsv3
46 | self.masked = masked
47 | self.length = length
48 |
--------------------------------------------------------------------------------
/slack_sdk/socket_mode/interval_runner.py:
--------------------------------------------------------------------------------
1 | import threading
2 | from threading import Thread, Event
3 | from typing import Callable
4 |
5 |
6 | class IntervalRunner:
7 | event: Event
8 | thread: Thread
9 |
10 | def __init__(self, target: Callable[[], None], interval_seconds: float = 0.1):
11 | self.event = threading.Event()
12 | self.target = target
13 | self.interval_seconds = interval_seconds
14 | self.thread = threading.Thread(target=self._run)
15 | self.thread.daemon = True
16 |
17 | def _run(self) -> None:
18 | while not self.event.is_set():
19 | self.target()
20 | self.event.wait(self.interval_seconds)
21 |
22 | def start(self) -> "IntervalRunner":
23 | self.thread.start()
24 | return self
25 |
26 | def is_alive(self) -> bool:
27 | return self.thread is not None and self.thread.is_alive()
28 |
29 | def shutdown(self):
30 | if self.is_alive():
31 | self.event.set()
32 | self.thread.join()
33 | self.thread = None
34 |
--------------------------------------------------------------------------------
/slack_sdk/socket_mode/listeners.py:
--------------------------------------------------------------------------------
1 | from typing import Optional
2 |
3 | from slack_sdk.socket_mode.request import SocketModeRequest
4 |
5 |
6 | class WebSocketMessageListener:
7 | def __call__(
8 | client: "BaseSocketModeClient", # type: ignore[name-defined] # noqa: F821
9 | message: dict,
10 | raw_message: Optional[str] = None,
11 | ): # noqa: F821
12 | raise NotImplementedError()
13 |
14 |
15 | class SocketModeRequestListener:
16 | def __call__(client: "BaseSocketModeClient", request: SocketModeRequest): # type: ignore[name-defined] # noqa: F821, F821, E501
17 | raise NotImplementedError()
18 |
--------------------------------------------------------------------------------
/slack_sdk/socket_mode/logger/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/python-slack-sdk/443ca03e425d93f47822a4de5776ff0595f5ed3d/slack_sdk/socket_mode/logger/__init__.py
--------------------------------------------------------------------------------
/slack_sdk/socket_mode/logger/messages.py:
--------------------------------------------------------------------------------
1 | import re
2 |
3 |
4 | def debug_redacted_message_string(message: str) -> str:
5 | xwfp_token_pattern = re.compile(r"\"xwfp-[A-Za-z0-9\-]+\"") # ex: "xwfp-abc-ABC-1234"
6 | return re.sub(xwfp_token_pattern, "[[REDACTED]]", message)
7 |
--------------------------------------------------------------------------------
/slack_sdk/socket_mode/response.py:
--------------------------------------------------------------------------------
1 | from typing import Union, Optional
2 |
3 | from slack_sdk.models import JsonObject
4 |
5 |
6 | class SocketModeResponse:
7 | envelope_id: str
8 | payload: Optional[dict]
9 |
10 | def __init__(self, envelope_id: str, payload: Optional[Union[dict, JsonObject, str]] = None):
11 | self.envelope_id = envelope_id
12 |
13 | if payload is None:
14 | self.payload = None
15 | elif isinstance(payload, JsonObject):
16 | self.payload = payload.to_dict()
17 | elif isinstance(payload, dict):
18 | self.payload = payload
19 | elif isinstance(payload, str):
20 | self.payload = {"text": payload}
21 | else:
22 | raise ValueError(f"Unsupported payload data type ({type(payload)})")
23 |
24 | def to_dict(self) -> dict:
25 | d = {"envelope_id": self.envelope_id}
26 | if self.payload is not None:
27 | d["payload"] = self.payload # type: ignore[assignment]
28 | return d
29 |
--------------------------------------------------------------------------------
/slack_sdk/version.py:
--------------------------------------------------------------------------------
1 | """Check the latest version at https://pypi.org/project/slack-sdk/"""
2 |
3 | __version__ = "3.35.0"
4 |
--------------------------------------------------------------------------------
/slack_sdk/web/__init__.py:
--------------------------------------------------------------------------------
1 | """The Slack Web API allows you to build applications that interact with Slack
2 | in more complex ways than the integrations we provide out of the box."""
3 | from .client import WebClient
4 | from .slack_response import SlackResponse
5 |
6 | __all__ = [
7 | "WebClient",
8 | "SlackResponse",
9 | ]
10 |
--------------------------------------------------------------------------------
/slack_sdk/web/file_upload_v2_result.py:
--------------------------------------------------------------------------------
1 | class FileUploadV2Result:
2 | status: int
3 | body: str
4 |
5 | def __init__(self, status: int, body: str):
6 | self.status = status
7 | self.body = body
8 |
--------------------------------------------------------------------------------
/slack_sdk/webhook/__init__.py:
--------------------------------------------------------------------------------
1 | """You can use slack_sdk.webhook.WebhookClient for Incoming Webhooks
2 | and message responses using response_url in payloads.
3 | """
4 | # from .async_client import AsyncWebhookClient
5 | from .client import WebhookClient
6 | from .webhook_response import WebhookResponse
7 |
8 | __all__ = [
9 | "WebhookClient",
10 | "WebhookResponse",
11 | ]
12 |
--------------------------------------------------------------------------------
/slack_sdk/webhook/internal_utils.py:
--------------------------------------------------------------------------------
1 | import logging
2 | from typing import Optional, Dict, Any
3 |
4 | from slack_sdk.web.internal_utils import (
5 | _parse_web_class_objects,
6 | get_user_agent,
7 | )
8 | from .webhook_response import WebhookResponse
9 |
10 |
11 | def _build_body(original_body: Optional[Dict[str, Any]]) -> Optional[Dict[str, Any]]:
12 | if original_body:
13 | body = {k: v for k, v in original_body.items() if v is not None}
14 | _parse_web_class_objects(body)
15 | return body
16 | return None
17 |
18 |
19 | def _build_request_headers(
20 | default_headers: Dict[str, str],
21 | additional_headers: Optional[Dict[str, str]],
22 | ) -> Dict[str, str]:
23 | if default_headers is None and additional_headers is None:
24 | return {}
25 |
26 | request_headers = {
27 | "Content-Type": "application/json;charset=utf-8",
28 | }
29 | if default_headers is None or "User-Agent" not in default_headers:
30 | request_headers["User-Agent"] = get_user_agent()
31 |
32 | request_headers.update(default_headers)
33 | if additional_headers:
34 | request_headers.update(additional_headers)
35 | return request_headers
36 |
37 |
38 | def _debug_log_response(logger, resp: WebhookResponse) -> None:
39 | if logger.level <= logging.DEBUG:
40 | logger.debug(
41 | "Received the following response - "
42 | f"status: {resp.status_code}, "
43 | f"headers: {(dict(resp.headers))}, "
44 | f"body: {resp.body}"
45 | )
46 |
--------------------------------------------------------------------------------
/slack_sdk/webhook/webhook_response.py:
--------------------------------------------------------------------------------
1 | from typing import Dict, Any
2 |
3 |
4 | class WebhookResponse:
5 | def __init__(
6 | self,
7 | *,
8 | url: str,
9 | status_code: int,
10 | body: str,
11 | headers: Dict[str, Any],
12 | ):
13 | self.api_url = url
14 | self.status_code = status_code
15 | self.body = body
16 | self.headers = headers
17 |
--------------------------------------------------------------------------------
/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/python-slack-sdk/443ca03e425d93f47822a4de5776ff0595f5ed3d/tests/__init__.py
--------------------------------------------------------------------------------
/tests/data/channel.created.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "channel_created",
3 | "channel": {
4 | "id": "C024BE91L",
5 | "name": "fun",
6 | "created": 1360782804,
7 | "creator": "U024BE7LH"
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/tests/data/im.created.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "im_created",
3 | "user": "U024BE7LH",
4 | "channel": {
5 | "id": "D024BE91L",
6 | "user": "U123BL234",
7 | "created": 1360782804
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/tests/data/slack_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/python-slack-sdk/443ca03e425d93f47822a4de5776ff0595f5ed3d/tests/data/slack_logo.png
--------------------------------------------------------------------------------
/tests/data/slack_logo_new.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/python-slack-sdk/443ca03e425d93f47822a4de5776ff0595f5ed3d/tests/data/slack_logo_new.png
--------------------------------------------------------------------------------
/tests/data/view_home_006.json:
--------------------------------------------------------------------------------
1 | {
2 | "id": "VMHU10V25",
3 | "team_id": "T8N4K1JN",
4 | "type": "home",
5 | "blocks": [
6 | {
7 | "type": "section",
8 | "block_id": "2WGp9",
9 | "text": {
10 | "type": "mrkdwn",
11 | "text": "A simple section with some sample sentence.",
12 | "verbatim": false
13 | }
14 | }
15 | ],
16 | "private_metadata": "Shh it is a secret",
17 | "callback_id": "identify_your_home_tab",
18 | "hash": "156772938.1827394",
19 | "clear_on_close": false,
20 | "notify_on_close": false,
21 | "root_view_id": "VMHU10V25",
22 | "app_id": "AA4928AQ",
23 | "external_id": "some-unique-id",
24 | "bot_id": "BA13894H"
25 | }
--------------------------------------------------------------------------------
/tests/data/view_modal_008.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "modal",
3 | "title": {
4 | "type": "plain_text",
5 | "text": "My App",
6 | "emoji": true
7 | },
8 | "submit": {
9 | "type": "plain_text",
10 | "text": "Submit",
11 | "emoji": true
12 | },
13 | "close": {
14 | "type": "plain_text",
15 | "text": "Cancel",
16 | "emoji": true
17 | },
18 | "private_metadata": "something important here",
19 | "blocks": [
20 | {
21 | "type": "section",
22 | "text": {
23 | "type": "mrkdwn",
24 | "text": "This is a mrkdwn section block :ghost: *this is bold*, and ~this is crossed out~, and "
25 | }
26 | }
27 | ],
28 | "state": {
29 | "values": {
30 | "multi-line": {
31 | "ml-value": {
32 | "type": "plain_text_input",
33 | "value": "This is my example inputted value"
34 | }
35 | }
36 | }
37 | },
38 | "hash": "156663117.cd33ad1f"
39 | }
--------------------------------------------------------------------------------
/tests/data/view_modal_009.json:
--------------------------------------------------------------------------------
1 | {
2 | "id": "VNM522E2U",
3 | "team_id": "T9M4RL1JM",
4 | "type": "modal",
5 | "title": {
6 | "type": "plain_text",
7 | "text": "Pushed Modal",
8 | "emoji": true
9 | },
10 | "close": {
11 | "type": "plain_text",
12 | "text": "Back",
13 | "emoji": true
14 | },
15 | "submit": {
16 | "type": "plain_text",
17 | "text": "Save",
18 | "emoji": true
19 | },
20 | "blocks": [
21 | {
22 | "type": "input",
23 | "block_id": "edit_details",
24 | "element": {
25 | "type": "plain_text_input",
26 | "action_id": "detail_input"
27 | },
28 | "label": {
29 | "type": "plain_text",
30 | "text": "Edit details"
31 | }
32 | }
33 | ],
34 | "private_metadata": "secret",
35 | "callback_id": "view_4",
36 | "external_id": "some-unique-id",
37 | "state": {
38 | "values": {}
39 | },
40 | "hash": "1569362015.55b5e41b",
41 | "clear_on_close": true,
42 | "notify_on_close": false,
43 | "root_view_id": "VNN729E3U",
44 | "app_id": "AAD3351BQ",
45 | "bot_id": "BADF7A34H"
46 | }
--------------------------------------------------------------------------------
/tests/data/view_modal_010.json:
--------------------------------------------------------------------------------
1 | {
2 | "id": "VMHU10V25",
3 | "team_id": "T8N4K1JN",
4 | "type": "modal",
5 | "title": {
6 | "type": "plain_text",
7 | "text": "Quite a plain modal"
8 | },
9 | "submit": {
10 | "type": "plain_text",
11 | "text": "Create"
12 | },
13 | "blocks": [
14 | {
15 | "type": "input",
16 | "block_id": "a_block_id",
17 | "label": {
18 | "type": "plain_text",
19 | "text": "A simple label",
20 | "emoji": true
21 | },
22 | "optional": false,
23 | "element": {
24 | "type": "plain_text_input",
25 | "action_id": "an_action_id"
26 | }
27 | }
28 | ],
29 | "private_metadata": "Shh it is a secret",
30 | "callback_id": "identify_your_modals",
31 | "external_id": "some-unique-id",
32 | "state": {
33 | "values": {}
34 | },
35 | "hash": "156772938.1827394",
36 | "clear_on_close": false,
37 | "notify_on_close": false,
38 | "root_view_id": "VMHU10V25",
39 | "app_id": "AA4928AQ",
40 | "bot_id": "BA13894H"
41 | }
--------------------------------------------------------------------------------
/tests/data/web_response_api_test.json:
--------------------------------------------------------------------------------
1 | {
2 | "ok": true
3 | }
--------------------------------------------------------------------------------
/tests/data/web_response_api_test_false.json:
--------------------------------------------------------------------------------
1 | {
2 | "ok": false
3 | }
--------------------------------------------------------------------------------
/tests/data/web_response_channels_list_pagination.json:
--------------------------------------------------------------------------------
1 | {
2 | "ok": true,
3 | "channels": [
4 | {
5 | "id": "C1"
6 | }
7 | ],
8 | "response_metadata": {
9 | "next_cursor": "has_page2"
10 | }
11 | }
--------------------------------------------------------------------------------
/tests/data/web_response_channels_list_pagination2.json:
--------------------------------------------------------------------------------
1 | {
2 | "ok": true,
3 | "channels": [
4 | {
5 | "id": "C2"
6 | }
7 | ],
8 | "response_metadata": {
9 | "next_cursor": "page2"
10 | }
11 | }
--------------------------------------------------------------------------------
/tests/data/web_response_channels_list_pagination2_page2.json:
--------------------------------------------------------------------------------
1 | {
2 | "ok": true,
3 | "channels": [
4 | {
5 | "id": "C3"
6 | }
7 | ]
8 | }
--------------------------------------------------------------------------------
/tests/data/web_response_channels_list_pagination_has_page2.json:
--------------------------------------------------------------------------------
1 | {
2 | "ok": true,
3 | "channels": [
4 | {
5 | "id": "C2"
6 | }
7 | ],
8 | "response_metadata": {
9 | "next_cursor": "has_page3"
10 | }
11 | }
--------------------------------------------------------------------------------
/tests/data/web_response_channels_list_pagination_has_page3.json:
--------------------------------------------------------------------------------
1 | {
2 | "ok": true,
3 | "channels": [
4 | {
5 | "id": "C3"
6 | }
7 | ]
8 | }
--------------------------------------------------------------------------------
/tests/data/web_response_users_list_pagination.json:
--------------------------------------------------------------------------------
1 | {
2 | "ok": true,
3 | "members": [
4 | "Bob",
5 | "cat"
6 | ],
7 | "response_metadata": {
8 | "next_cursor": 1
9 | }
10 | }
--------------------------------------------------------------------------------
/tests/data/web_response_users_list_pagination_1.json:
--------------------------------------------------------------------------------
1 | {
2 | "ok": true,
3 | "members": [
4 | "Kevin",
5 | "dog"
6 | ],
7 | "response_metadata": {
8 | "next_cursor": ""
9 | }
10 | }
--------------------------------------------------------------------------------
/tests/data/web_response_users_setPhoto.json:
--------------------------------------------------------------------------------
1 | {
2 | "ok": true
3 | }
--------------------------------------------------------------------------------
/tests/data/日本語.txt:
--------------------------------------------------------------------------------
1 | 日本語の文書です。
--------------------------------------------------------------------------------
/tests/helpers.py:
--------------------------------------------------------------------------------
1 | import asyncio
2 | import copy
3 | import os
4 | import sys
5 | from typing import Any
6 |
7 |
8 | def async_test(coro):
9 | loop = asyncio.new_event_loop()
10 | asyncio.set_event_loop(loop)
11 |
12 | def wrapper(*args, **kwargs):
13 | future = coro(*args, **kwargs)
14 | return asyncio.get_event_loop().run_until_complete(future)
15 |
16 | return wrapper
17 |
18 |
19 | def remove_os_env_temporarily() -> dict:
20 | old_env = os.environ.copy()
21 | os.environ.clear()
22 | for key, value in old_env.items():
23 | if key.startswith("PYTHON_SLACK_SDK_"):
24 | os.environ[key] = value
25 | return old_env
26 |
27 |
28 | def restore_os_env(old_env: dict) -> None:
29 | os.environ.update(old_env)
30 |
31 |
32 | def create_copy(original: Any) -> Any:
33 | if sys.version_info.major == 3 and sys.version_info.minor <= 6:
34 | # NOTE: Unfortunately, copy.deepcopy doesn't work in Python 3.6.5.
35 | # --------------------
36 | # > rv = reductor(4)
37 | # E TypeError: can't pickle _thread.RLock objects
38 | # ../../.pyenv/versions/3.6.10/lib/python3.6/copy.py:169: TypeError
39 | # --------------------
40 | # As a workaround, this operation uses shallow copies in Python 3.6.
41 | # If your code modifies the shared data in threads / async functions, race conditions may arise.
42 | # Please consider upgrading Python major version to 3.7+ if you encounter some issues due to this.
43 | return copy.copy(original)
44 | else:
45 | return copy.deepcopy(original)
46 |
--------------------------------------------------------------------------------
/tests/mock_web_api_server/mock_server_thread.py:
--------------------------------------------------------------------------------
1 | from asyncio import Queue
2 | import asyncio
3 | from http.server import HTTPServer, SimpleHTTPRequestHandler
4 | import threading
5 | from typing import Type, Union
6 | from unittest import TestCase
7 |
8 |
9 | class MockServerThread(threading.Thread):
10 | def __init__(
11 | self, queue: Union[Queue, asyncio.Queue], test: TestCase, handler: Type[SimpleHTTPRequestHandler], port: int = 8888
12 | ):
13 | threading.Thread.__init__(self)
14 | self.handler = handler
15 | self.test = test
16 | self.queue = queue
17 | self.port = port
18 |
19 | def run(self):
20 | self.server = HTTPServer(("localhost", self.port), self.handler)
21 | self.server.queue = self.queue
22 | self.test.server_url = f"http://localhost:{str(self.port)}"
23 | self.test.host, self.test.port = self.server.socket.getsockname()
24 | self.test.server_started.set() # threading.Event()
25 |
26 | self.test = None
27 | try:
28 | self.server.serve_forever(0.05)
29 | finally:
30 | self.server.server_close()
31 |
32 | def stop(self):
33 | with self.server.queue.mutex:
34 | del self.server.queue
35 | self.server.shutdown()
36 | self.join()
37 |
38 | def stop_unsafe(self):
39 | del self.server.queue
40 | self.server.shutdown()
41 | self.join()
42 |
--------------------------------------------------------------------------------
/tests/mock_web_api_server/received_requests.py:
--------------------------------------------------------------------------------
1 | import asyncio
2 | from queue import Queue
3 | from typing import Optional, Union
4 |
5 |
6 | class ReceivedRequests:
7 | def __init__(self, queue: Union[Queue, asyncio.Queue]):
8 | self.queue = queue
9 | self.received_requests: dict = {}
10 |
11 | def get(self, key: str, default: Optional[int] = None) -> Optional[int]:
12 | while not self.queue.empty():
13 | path = self.queue.get()
14 | self.received_requests[path] = self.received_requests.get(path, 0) + 1
15 | return self.received_requests.get(key, default)
16 |
17 | async def get_async(self, key: str, default: Optional[int] = None) -> Optional[int]:
18 | while not self.queue.empty():
19 | path = await self.queue.get()
20 | self.received_requests[path] = self.received_requests.get(path, 0) + 1
21 | return self.received_requests.get(key, default)
22 |
--------------------------------------------------------------------------------
/tests/slack_sdk/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/python-slack-sdk/443ca03e425d93f47822a4de5776ff0595f5ed3d/tests/slack_sdk/__init__.py
--------------------------------------------------------------------------------
/tests/slack_sdk/audit_logs/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/python-slack-sdk/443ca03e425d93f47822a4de5776ff0595f5ed3d/tests/slack_sdk/audit_logs/__init__.py
--------------------------------------------------------------------------------
/tests/slack_sdk/audit_logs/test_client_http_retry.py:
--------------------------------------------------------------------------------
1 | import unittest
2 |
3 | from slack_sdk.audit_logs import AuditLogsClient
4 | from slack_sdk.http_retry import RateLimitErrorRetryHandler
5 | from tests.slack_sdk.audit_logs.mock_web_api_handler import MockHandler
6 | from tests.mock_web_api_server import setup_mock_web_api_server, cleanup_mock_web_api_server
7 | from ..my_retry_handler import MyRetryHandler
8 |
9 |
10 | class TestAuditLogsClient_HttpRetries(unittest.TestCase):
11 | def setUp(self):
12 | setup_mock_web_api_server(self, MockHandler)
13 |
14 | def tearDown(self):
15 | cleanup_mock_web_api_server(self)
16 |
17 | def test_retries(self):
18 | retry_handler = MyRetryHandler(max_retry_count=2)
19 | client = AuditLogsClient(
20 | token="xoxp-remote_disconnected",
21 | base_url="http://localhost:8888/",
22 | retry_handlers=[retry_handler],
23 | )
24 | try:
25 | client.actions()
26 | self.fail("An exception is expected")
27 | except Exception as _:
28 | pass
29 |
30 | self.assertEqual(2, retry_handler.call_count)
31 |
32 | def test_ratelimited(self):
33 | client = AuditLogsClient(
34 | token="xoxp-ratelimited",
35 | base_url="http://localhost:8888/",
36 | )
37 | client.retry_handlers.append(RateLimitErrorRetryHandler())
38 |
39 | response = client.actions()
40 | # Just running retries; no assertions for call count so far
41 | self.assertEqual(429, response.status_code)
42 |
--------------------------------------------------------------------------------
/tests/slack_sdk/fatal_error_retry_handler.py:
--------------------------------------------------------------------------------
1 | from typing import Optional
2 |
3 | from slack_sdk.http_retry.interval_calculator import RetryIntervalCalculator
4 | from slack_sdk.http_retry.state import RetryState
5 | from slack_sdk.http_retry.request import HttpRequest
6 | from slack_sdk.http_retry.response import HttpResponse
7 | from slack_sdk.http_retry.handler import RetryHandler, default_interval_calculator
8 |
9 |
10 | class FatalErrorRetryHandler(RetryHandler):
11 | def __init__(
12 | self,
13 | max_retry_count: int = 1,
14 | interval_calculator: RetryIntervalCalculator = default_interval_calculator,
15 | ):
16 | super().__init__(max_retry_count, interval_calculator)
17 | self.call_count = 0
18 |
19 | def _can_retry(
20 | self,
21 | *,
22 | state: RetryState,
23 | request: HttpRequest,
24 | response: Optional[HttpResponse],
25 | error: Optional[Exception],
26 | ) -> bool:
27 | self.call_count += 1
28 | return response is not None and response.status_code == 200 and response.body.get("error") == "fatal_error"
29 |
--------------------------------------------------------------------------------
/tests/slack_sdk/http_retry/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/python-slack-sdk/443ca03e425d93f47822a4de5776ff0595f5ed3d/tests/slack_sdk/http_retry/__init__.py
--------------------------------------------------------------------------------
/tests/slack_sdk/http_retry/test_builtins.py:
--------------------------------------------------------------------------------
1 | import unittest
2 |
3 | from slack_sdk.http_retry import (
4 | FixedValueRetryIntervalCalculator,
5 | default_retry_handlers,
6 | all_builtin_retry_handlers,
7 | )
8 |
9 |
10 | class TestBuiltins(unittest.TestCase):
11 | def test_default_ones(self):
12 | list = default_retry_handlers()
13 | self.assertEqual(1, len(list))
14 | list.clear()
15 | self.assertEqual(0, len(list))
16 | list = default_retry_handlers()
17 | self.assertEqual(1, len(list))
18 |
19 | list = all_builtin_retry_handlers()
20 | self.assertEqual(2, len(list))
21 | list.clear()
22 | self.assertEqual(0, len(list))
23 | list = all_builtin_retry_handlers()
24 | self.assertEqual(2, len(list))
25 |
26 | def test_fixed_value_retry_interval_calculator(self):
27 | for fixed_value in [0.1, 0.2]:
28 | calculator = FixedValueRetryIntervalCalculator(fixed_internal=fixed_value)
29 | for i in range(10):
30 | self.assertEqual(fixed_value, calculator.calculate_sleep_duration(i))
31 |
--------------------------------------------------------------------------------
/tests/slack_sdk/models/test_init.py:
--------------------------------------------------------------------------------
1 | import unittest
2 |
3 | from slack_sdk.models import extract_json
4 | from slack_sdk.models.blocks import PlainTextObject, MarkdownTextObject
5 |
6 |
7 | class TestInit(unittest.TestCase):
8 | def test_from_list_of_json_objects(self):
9 | json_objects = [
10 | PlainTextObject.from_str("foo"),
11 | MarkdownTextObject.from_str("bar"),
12 | ]
13 | output = extract_json(json_objects)
14 | expected = {
15 | "result": [
16 | {"type": "plain_text", "text": "foo", "emoji": True},
17 | {"type": "mrkdwn", "text": "bar"},
18 | ]
19 | }
20 | self.assertDictEqual(expected, {"result": output})
21 |
22 | def test_from_single_json_object(self):
23 | single_json_object = PlainTextObject.from_str("foo")
24 | output = extract_json(single_json_object)
25 | expected = {"result": {"type": "plain_text", "text": "foo", "emoji": True}}
26 | self.assertDictEqual(expected, {"result": output})
27 |
--------------------------------------------------------------------------------
/tests/slack_sdk/my_retry_handler.py:
--------------------------------------------------------------------------------
1 | from http.client import RemoteDisconnected
2 | from typing import Optional
3 | from slack_sdk.http_retry.interval_calculator import RetryIntervalCalculator
4 | from slack_sdk.http_retry.state import RetryState
5 | from slack_sdk.http_retry.request import HttpRequest
6 | from slack_sdk.http_retry.response import HttpResponse
7 | from slack_sdk.http_retry.handler import RetryHandler, default_interval_calculator
8 |
9 |
10 | class MyRetryHandler(RetryHandler):
11 | def __init__(
12 | self,
13 | max_retry_count: int = 1,
14 | interval_calculator: RetryIntervalCalculator = default_interval_calculator,
15 | ):
16 | super().__init__(max_retry_count, interval_calculator)
17 | self.call_count = 0
18 |
19 | def _can_retry(
20 | self,
21 | *,
22 | state: RetryState,
23 | request: HttpRequest,
24 | response: Optional[HttpResponse],
25 | error: Optional[Exception],
26 | ) -> bool:
27 | self.call_count += 1
28 | if error is None:
29 | return False
30 | for error_type in [ConnectionResetError, RemoteDisconnected]:
31 | if isinstance(error, error_type):
32 | return True
33 | return False
34 |
--------------------------------------------------------------------------------
/tests/slack_sdk/oauth/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/python-slack-sdk/443ca03e425d93f47822a4de5776ff0595f5ed3d/tests/slack_sdk/oauth/__init__.py
--------------------------------------------------------------------------------
/tests/slack_sdk/oauth/authorize_url_generator/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/python-slack-sdk/443ca03e425d93f47822a4de5776ff0595f5ed3d/tests/slack_sdk/oauth/authorize_url_generator/__init__.py
--------------------------------------------------------------------------------
/tests/slack_sdk/oauth/installation_store/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/python-slack-sdk/443ca03e425d93f47822a4de5776ff0595f5ed3d/tests/slack_sdk/oauth/installation_store/__init__.py
--------------------------------------------------------------------------------
/tests/slack_sdk/oauth/installation_store/test_interface.py:
--------------------------------------------------------------------------------
1 | import unittest
2 |
3 | from slack_sdk.oauth.installation_store import InstallationStore
4 | from slack_sdk.oauth.installation_store.async_installation_store import (
5 | AsyncInstallationStore,
6 | )
7 |
8 |
9 | class TestInterface(unittest.TestCase):
10 | def setUp(self):
11 | pass
12 |
13 | def tearDown(self):
14 | pass
15 |
16 | def test_sync(self):
17 | store = InstallationStore()
18 | self.assertIsNotNone(store)
19 |
20 | def test_async(self):
21 | store = AsyncInstallationStore()
22 | self.assertIsNotNone(store)
23 |
--------------------------------------------------------------------------------
/tests/slack_sdk/oauth/redirect_uri_page_renderer/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/python-slack-sdk/443ca03e425d93f47822a4de5776ff0595f5ed3d/tests/slack_sdk/oauth/redirect_uri_page_renderer/__init__.py
--------------------------------------------------------------------------------
/tests/slack_sdk/oauth/redirect_uri_page_renderer/test_init.py:
--------------------------------------------------------------------------------
1 | import unittest
2 |
3 | from slack_sdk.oauth import RedirectUriPageRenderer
4 |
5 |
6 | class TestInit(unittest.TestCase):
7 | def setUp(self):
8 | pass
9 |
10 | def tearDown(self):
11 | pass
12 |
13 | def test_render_failure_page(self):
14 | renderer = RedirectUriPageRenderer(
15 | install_path="/slack/install",
16 | redirect_uri_path="/slack/oauth_redirect",
17 | )
18 | page = renderer.render_failure_page("something-wrong")
19 | self.assertTrue("Something Went Wrong!" in page)
20 |
21 | def test_render_success_page(self):
22 | renderer = RedirectUriPageRenderer(
23 | install_path="/slack/install",
24 | redirect_uri_path="/slack/oauth_redirect",
25 | )
26 | page = renderer.render_success_page(app_id="A111", team_id="T111")
27 | self.assertTrue("slack://app?team=T111&id=A111" in page)
28 |
29 | page = renderer.render_success_page(app_id="A111", team_id=None)
30 | self.assertTrue("slack://open" in page)
31 |
--------------------------------------------------------------------------------
/tests/slack_sdk/oauth/state_store/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/python-slack-sdk/443ca03e425d93f47822a4de5776ff0595f5ed3d/tests/slack_sdk/oauth/state_store/__init__.py
--------------------------------------------------------------------------------
/tests/slack_sdk/oauth/state_store/test_amazon_s3.py:
--------------------------------------------------------------------------------
1 | import unittest
2 |
3 | import boto3
4 |
5 | try:
6 | from moto import mock_aws
7 | except ImportError:
8 | from moto import mock_s3 as mock_aws
9 | from slack_sdk.oauth.state_store.amazon_s3 import AmazonS3OAuthStateStore
10 |
11 |
12 | class TestAmazonS3(unittest.TestCase):
13 | mock_aws = mock_aws()
14 | bucket_name = "test-bucket"
15 |
16 | def setUp(self):
17 | self.mock_aws.start()
18 | s3 = boto3.resource("s3")
19 | bucket = s3.Bucket(self.bucket_name)
20 | bucket.create(CreateBucketConfiguration={"LocationConstraint": "af-south-1"})
21 |
22 | def tearDown(self):
23 | self.mock_aws.stop()
24 |
25 | def test_instance(self):
26 | store = AmazonS3OAuthStateStore(
27 | s3_client=boto3.client("s3"),
28 | bucket_name=self.bucket_name,
29 | expiration_seconds=10,
30 | )
31 | self.assertIsNotNone(store)
32 |
33 | def test_issue_and_consume(self):
34 | store = AmazonS3OAuthStateStore(
35 | s3_client=boto3.client("s3"),
36 | bucket_name=self.bucket_name,
37 | expiration_seconds=10,
38 | )
39 | state = store.issue()
40 | result = store.consume(state)
41 | self.assertTrue(result)
42 | result = store.consume(state)
43 | self.assertFalse(result)
44 |
--------------------------------------------------------------------------------
/tests/slack_sdk/oauth/state_store/test_async_sqlalchemy.py:
--------------------------------------------------------------------------------
1 | import asyncio
2 | import unittest
3 | from tests.helpers import async_test
4 | from sqlalchemy.ext.asyncio import AsyncEngine, create_async_engine
5 |
6 | from slack_sdk.oauth.state_store.sqlalchemy import AsyncSQLAlchemyOAuthStateStore
7 |
8 |
9 | class TestSQLAlchemy(unittest.TestCase):
10 | engine: AsyncEngine
11 |
12 | @async_test
13 | async def setUp(self):
14 | self.engine = create_async_engine("sqlite+aiosqlite:///:memory:")
15 | self.store = AsyncSQLAlchemyOAuthStateStore(engine=self.engine, expiration_seconds=2)
16 | async with self.engine.begin() as conn:
17 | await conn.run_sync(self.store.metadata.create_all)
18 |
19 | @async_test
20 | async def tearDown(self):
21 | async with self.engine.begin() as conn:
22 | await conn.run_sync(self.store.metadata.drop_all)
23 | await self.engine.dispose()
24 |
25 | @async_test
26 | async def test_issue_and_consume(self):
27 | state = await self.store.async_issue()
28 | result = await self.store.async_consume(state)
29 | self.assertTrue(result)
30 | result = await self.store.async_consume(state)
31 | self.assertFalse(result)
32 |
33 | @async_test
34 | async def test_expiration(self):
35 | state = await self.store.async_issue()
36 | await asyncio.sleep(3)
37 | result = await self.store.async_consume(state)
38 | self.assertFalse(result)
39 |
--------------------------------------------------------------------------------
/tests/slack_sdk/oauth/state_store/test_file.py:
--------------------------------------------------------------------------------
1 | import unittest
2 |
3 | from slack_sdk.oauth.state_store import FileOAuthStateStore
4 |
5 |
6 | class TestFile(unittest.TestCase):
7 | def setUp(self):
8 | pass
9 |
10 | def tearDown(self):
11 | pass
12 |
13 | def test_instance(self):
14 | store = FileOAuthStateStore(expiration_seconds=10)
15 | self.assertIsNotNone(store)
16 |
17 | def test_issue_and_consume(self):
18 | store = FileOAuthStateStore(expiration_seconds=10)
19 | state = store.issue()
20 | result = store.consume(state)
21 | self.assertTrue(result)
22 | result = store.consume(state)
23 | self.assertFalse(result)
24 |
25 | def test_kwargs(self):
26 | store = FileOAuthStateStore(expiration_seconds=10)
27 | store.issue(foo=123, bar="baz")
28 |
--------------------------------------------------------------------------------
/tests/slack_sdk/oauth/state_store/test_sqlalchemy.py:
--------------------------------------------------------------------------------
1 | import time
2 | import unittest
3 |
4 | import sqlalchemy
5 | from sqlalchemy.engine import Engine
6 |
7 | from slack_sdk.oauth.state_store.sqlalchemy import SQLAlchemyOAuthStateStore
8 |
9 |
10 | class TestSQLAlchemy(unittest.TestCase):
11 | engine: Engine
12 |
13 | def setUp(self):
14 | self.engine = sqlalchemy.create_engine("sqlite:///:memory:")
15 | self.store = SQLAlchemyOAuthStateStore(engine=self.engine, expiration_seconds=2)
16 | self.store.metadata.create_all(self.engine)
17 |
18 | def tearDown(self):
19 | self.store.metadata.drop_all(self.engine)
20 | self.engine.dispose()
21 |
22 | def test_issue_and_consume(self):
23 | state = self.store.issue()
24 | result = self.store.consume(state)
25 | self.assertTrue(result)
26 | result = self.store.consume(state)
27 | self.assertFalse(result)
28 |
29 | def test_expiration(self):
30 | state = self.store.issue()
31 | time.sleep(3)
32 | result = self.store.consume(state)
33 | self.assertFalse(result)
34 |
--------------------------------------------------------------------------------
/tests/slack_sdk/oauth/state_store/test_sqlite3.py:
--------------------------------------------------------------------------------
1 | import unittest
2 |
3 | from slack_sdk.oauth.state_store.sqlite3 import SQLite3OAuthStateStore
4 |
5 |
6 | class TestSQLite3(unittest.TestCase):
7 | def setUp(self):
8 | pass
9 |
10 | def tearDown(self):
11 | pass
12 |
13 | def test_instance(self):
14 | store = SQLite3OAuthStateStore(
15 | database="logs/test.db",
16 | expiration_seconds=10,
17 | )
18 | self.assertIsNotNone(store)
19 |
20 | def test_issue_and_consume(self):
21 | store = SQLite3OAuthStateStore(
22 | database="logs/test.db",
23 | expiration_seconds=10,
24 | )
25 | state = store.issue()
26 | result = store.consume(state)
27 | self.assertTrue(result)
28 | result = store.consume(state)
29 | self.assertFalse(result)
30 |
--------------------------------------------------------------------------------
/tests/slack_sdk/oauth/state_utils/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/python-slack-sdk/443ca03e425d93f47822a4de5776ff0595f5ed3d/tests/slack_sdk/oauth/state_utils/__init__.py
--------------------------------------------------------------------------------
/tests/slack_sdk/oauth/state_utils/test_utils.py:
--------------------------------------------------------------------------------
1 | import unittest
2 |
3 | from slack_sdk.oauth import OAuthStateUtils
4 |
5 |
6 | class TestUtils(unittest.TestCase):
7 | def setUp(self):
8 | pass
9 |
10 | def tearDown(self):
11 | pass
12 |
13 | def test_is_valid_browser(self):
14 | utils = OAuthStateUtils()
15 | cookie_name = OAuthStateUtils.default_cookie_name
16 | result = utils.is_valid_browser("state-value", {"cookie": f"{cookie_name}=state-value"})
17 | self.assertTrue(result)
18 | result = utils.is_valid_browser("state-value", {"cookie": f"{cookie_name}=xxx"})
19 | self.assertFalse(result)
20 |
21 | result = utils.is_valid_browser("state-value", {"cookie": [f"{cookie_name}=state-value"]})
22 | self.assertTrue(result)
23 | result = utils.is_valid_browser("state-value", {"cookie": [f"{cookie_name}=xxx"]})
24 | self.assertFalse(result)
25 |
26 | def test_build_set_cookie_for_new_state(self):
27 | utils = OAuthStateUtils()
28 | value = utils.build_set_cookie_for_new_state("state-value")
29 | expected = "slack-app-oauth-state=state-value; Secure; HttpOnly; Path=/; Max-Age=600"
30 | self.assertEqual(expected, value)
31 |
32 | def test_build_set_cookie_for_deletion(self):
33 | utils = OAuthStateUtils()
34 | value = utils.build_set_cookie_for_deletion()
35 | expected = "slack-app-oauth-state=deleted; Secure; HttpOnly; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT"
36 | self.assertEqual(expected, value)
37 |
--------------------------------------------------------------------------------
/tests/slack_sdk/oauth/test_init.py:
--------------------------------------------------------------------------------
1 | import unittest
2 |
3 | from slack_sdk.oauth import AuthorizeUrlGenerator
4 |
5 |
6 | class TestInit(unittest.TestCase):
7 | def setUp(self):
8 | pass
9 |
10 | def tearDown(self):
11 | pass
12 |
13 | def test_generator(self):
14 | generator = AuthorizeUrlGenerator(
15 | client_id="111.222",
16 | scopes=["chat:write", "commands"],
17 | user_scopes=["search:read"],
18 | )
19 | url = generator.generate("state-value")
20 | expected = "https://slack.com/oauth/v2/authorize?state=state-value&client_id=111.222&scope=chat:write,commands&user_scope=search:read"
21 | self.assertEqual(expected, url)
22 |
--------------------------------------------------------------------------------
/tests/slack_sdk/oauth/token_rotation/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/python-slack-sdk/443ca03e425d93f47822a4de5776ff0595f5ed3d/tests/slack_sdk/oauth/token_rotation/__init__.py
--------------------------------------------------------------------------------
/tests/slack_sdk/rtm_v2/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/python-slack-sdk/443ca03e425d93f47822a4de5776ff0595f5ed3d/tests/slack_sdk/rtm_v2/__init__.py
--------------------------------------------------------------------------------
/tests/slack_sdk/scim/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/python-slack-sdk/443ca03e425d93f47822a4de5776ff0595f5ed3d/tests/slack_sdk/scim/__init__.py
--------------------------------------------------------------------------------
/tests/slack_sdk/scim/test_client_http_retry.py:
--------------------------------------------------------------------------------
1 | import unittest
2 |
3 | from slack_sdk.http_retry import RateLimitErrorRetryHandler
4 | from slack_sdk.scim import SCIMClient
5 | from tests.slack_sdk.scim.mock_web_api_handler import MockHandler
6 | from tests.mock_web_api_server import setup_mock_web_api_server, cleanup_mock_web_api_server
7 | from ..my_retry_handler import MyRetryHandler
8 |
9 |
10 | class TestSCIMClient(unittest.TestCase):
11 | def setUp(self):
12 | setup_mock_web_api_server(self, MockHandler)
13 |
14 | def tearDown(self):
15 | cleanup_mock_web_api_server(self)
16 |
17 | def test_retries(self):
18 | retry_handler = MyRetryHandler(max_retry_count=2)
19 | client = SCIMClient(
20 | base_url="http://localhost:8888/",
21 | token="xoxp-remote_disconnected",
22 | retry_handlers=[retry_handler],
23 | )
24 |
25 | try:
26 | client.search_users(start_index=0, count=1)
27 | self.fail("An exception is expected")
28 | except Exception as _:
29 | pass
30 |
31 | self.assertEqual(2, retry_handler.call_count)
32 |
33 | def test_ratelimited(self):
34 | client = SCIMClient(
35 | base_url="http://localhost:8888/",
36 | token="xoxp-ratelimited",
37 | )
38 | client.retry_handlers.append(RateLimitErrorRetryHandler())
39 |
40 | response = client.search_users(start_index=0, count=1)
41 | # Just running retries; no assertions for call count so far
42 | self.assertEqual(429, response.status_code)
43 |
--------------------------------------------------------------------------------
/tests/slack_sdk/scim/test_internals.py:
--------------------------------------------------------------------------------
1 | import json
2 | import unittest
3 |
4 | from slack_sdk.scim.v1.internal_utils import _to_snake_cased
5 |
6 |
7 | class TEstInternals(unittest.TestCase):
8 | def setUp(self):
9 | pass
10 |
11 | def tearDown(self):
12 | pass
13 |
14 | def test_snake_cased(self):
15 | response_body = """{"totalResults":441,"itemsPerPage":1,"startIndex":1,"schemas":["urn:scim:schemas:core:1.0"],"Resources":[{"schemas":["urn:scim:schemas:core:1.0"],"id":"W111","externalId":"","meta":{"created":"2020-08-13T04:15:35-07:00","location":"https://api.slack.com/scim/v1/Users/W111"},"userName":"test-app","nickName":"test-app","name":{"givenName":"","familyName":""},"displayName":"","profileUrl":"https://test-test-test.enterprise.slack.com/team/test-app","title":"","timezone":"America/Los_Angeles","active":true,"emails":[{"value":"botuser@slack-bots.com","primary":true}],"photos":[{"value":"https://secure.gravatar.com/avatar/xxx.jpg","type":"photo"}],"groups":[]}]}"""
16 | result = _to_snake_cased(json.loads(response_body))
17 | self.assertEqual(result["start_index"], 1)
18 | self.assertIsNotNone(result["resources"][0]["id"])
19 |
--------------------------------------------------------------------------------
/tests/slack_sdk/signature/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/python-slack-sdk/443ca03e425d93f47822a4de5776ff0595f5ed3d/tests/slack_sdk/signature/__init__.py
--------------------------------------------------------------------------------
/tests/slack_sdk/socket_mode/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/python-slack-sdk/443ca03e425d93f47822a4de5776ff0595f5ed3d/tests/slack_sdk/socket_mode/__init__.py
--------------------------------------------------------------------------------
/tests/slack_sdk/socket_mode/logger/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/python-slack-sdk/443ca03e425d93f47822a4de5776ff0595f5ed3d/tests/slack_sdk/socket_mode/logger/__init__.py
--------------------------------------------------------------------------------
/tests/slack_sdk/socket_mode/test_request.py:
--------------------------------------------------------------------------------
1 | import json
2 | import unittest
3 |
4 | from slack_sdk.socket_mode.request import SocketModeRequest
5 |
6 |
7 | class TestRequest(unittest.TestCase):
8 | def setUp(self):
9 | pass
10 |
11 | def tearDown(self):
12 | pass
13 |
14 | def test(self):
15 | body = json.loads(
16 | """{"envelope_id":"1d3c79ab-0ffb-41f3-a080-d19e85f53649","payload":{"token":"xxx","team_id":"T111","team_domain":"xxx","channel_id":"C03E94MKU","channel_name":"random","user_id":"U111","user_name":"seratch","command":"/hello-socket-mode","text":"","api_app_id":"A111","response_url":"https://hooks.slack.com/commands/T111/111/xxx","trigger_id":"111.222.xxx"},"type":"slash_commands","accepts_response_payload":true}"""
17 | )
18 | req = SocketModeRequest.from_dict(body)
19 | self.assertIsNotNone(req)
20 | self.assertEqual(req.envelope_id, "1d3c79ab-0ffb-41f3-a080-d19e85f53649")
21 |
--------------------------------------------------------------------------------
/tests/slack_sdk/socket_mode/test_response.py:
--------------------------------------------------------------------------------
1 | import json
2 | import unittest
3 |
4 | from slack_sdk.socket_mode.response import SocketModeResponse
5 |
6 |
7 | class TestResponse(unittest.TestCase):
8 | def setUp(self):
9 | pass
10 |
11 | def tearDown(self):
12 | pass
13 |
14 | def test_parser(self):
15 | text = """{"envelope_id":"1d3c79ab-0ffb-41f3-a080-d19e85f53649","payload":{"text":"Thanks!"}}"""
16 | body = json.loads(text)
17 | response = SocketModeResponse(body["envelope_id"], body["payload"])
18 | self.assertIsNotNone(response)
19 | self.assertEqual(response.envelope_id, "1d3c79ab-0ffb-41f3-a080-d19e85f53649")
20 | self.assertEqual(response.payload.get("text"), "Thanks!")
21 |
22 | def test_to_dict(self):
23 | response = SocketModeResponse(envelope_id="xxx", payload={"text": "hi"})
24 | self.assertDictEqual(response.to_dict(), {"envelope_id": "xxx", "payload": {"text": "hi"}})
25 |
--------------------------------------------------------------------------------
/tests/slack_sdk/socket_mode/test_websocket_client.py:
--------------------------------------------------------------------------------
1 | import unittest
2 |
3 | from slack_sdk.socket_mode.websocket_client import SocketModeClient
4 |
5 |
6 | class TestWebSocketClientLibrary(unittest.TestCase):
7 | def setUp(self):
8 | pass
9 |
10 | def tearDown(self):
11 | pass
12 |
13 | def test_init_close(self):
14 | client = SocketModeClient(app_token="xapp-A111-222-xyz")
15 | try:
16 | self.assertIsNotNone(client)
17 | self.assertFalse(client.is_connected())
18 | finally:
19 | client.close()
20 |
--------------------------------------------------------------------------------
/tests/slack_sdk/web/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/python-slack-sdk/443ca03e425d93f47822a4de5776ff0595f5ed3d/tests/slack_sdk/web/__init__.py
--------------------------------------------------------------------------------
/tests/slack_sdk/web/test_web_client_http_retry_connection.py:
--------------------------------------------------------------------------------
1 | import unittest
2 |
3 | from slack_sdk.web import WebClient
4 | from tests.slack_sdk.web.mock_web_api_http_retry_handler import MockHandler
5 | from tests.mock_web_api_server import setup_mock_web_api_server, cleanup_mock_web_api_server
6 |
7 |
8 | class TestWebClient_HttpRetry(unittest.TestCase):
9 | def setUp(self):
10 | setup_mock_web_api_server(self, MockHandler, port=8889)
11 |
12 | def tearDown(self):
13 | cleanup_mock_web_api_server(self)
14 |
15 | def test_remote_disconnected(self):
16 | client = WebClient(
17 | base_url="http://localhost:8889",
18 | token="xoxb-remote_disconnected",
19 | team_id="T111",
20 | )
21 | client.auth_test()
22 |
--------------------------------------------------------------------------------
/tests/slack_sdk/web/test_web_client_issue_1049.py:
--------------------------------------------------------------------------------
1 | import unittest
2 |
3 | from slack_sdk.web import WebClient
4 | from tests.slack_sdk.web.mock_web_api_handler import MockHandler
5 | from tests.mock_web_api_server import setup_mock_web_api_server, cleanup_mock_web_api_server
6 |
7 |
8 | class TestWebClient_Issue_1049(unittest.TestCase):
9 | def setUp(self):
10 | setup_mock_web_api_server(self, MockHandler)
11 |
12 | def tearDown(self):
13 | cleanup_mock_web_api_server(self)
14 |
15 | def test_the_pattern(self):
16 | client = WebClient(
17 | base_url="http://localhost:8888",
18 | token="xoxb-admin_convo_pagination",
19 | )
20 | pages = []
21 | for page in client.admin_conversations_search(query="announcement"):
22 | pages.append(page)
23 | self.assertEqual(len(pages), 2)
24 |
--------------------------------------------------------------------------------
/tests/slack_sdk/web/test_web_client_issue_829.py:
--------------------------------------------------------------------------------
1 | import unittest
2 |
3 | import slack_sdk.errors as err
4 | from slack_sdk.web import WebClient
5 | from tests.slack_sdk.web.mock_web_api_handler import MockHandler
6 | from tests.mock_web_api_server import setup_mock_web_api_server, cleanup_mock_web_api_server
7 |
8 |
9 | class TestWebClient_Issue_829(unittest.TestCase):
10 | def setUp(self):
11 | setup_mock_web_api_server(self, MockHandler)
12 |
13 | def tearDown(self):
14 | cleanup_mock_web_api_server(self)
15 |
16 | def test_html_response_body_issue_829(self):
17 | client = WebClient(base_url="http://localhost:8888")
18 | try:
19 | client.users_list(token="xoxb-error_html_response")
20 | self.fail("SlackApiError expected here")
21 | except err.SlackApiError as e:
22 | self.assertTrue(
23 | str(e).startswith("The request to the Slack API failed. (url: http://"),
24 | e,
25 | )
26 | self.assertIsInstance(e.response.status_code, int)
27 | self.assertFalse(e.response["ok"])
28 | self.assertTrue(
29 | e.response["error"].startswith("Received a response in a non-JSON format: bool:
29 | self.call_count += 1
30 | return response is not None and response.status_code == 200 and response.body.get("error") == "fatal_error"
31 |
--------------------------------------------------------------------------------
/tests/slack_sdk_async/helpers.py:
--------------------------------------------------------------------------------
1 | import asyncio
2 |
3 |
4 | def async_test(coro):
5 | loop = asyncio.new_event_loop()
6 | asyncio.set_event_loop(loop)
7 |
8 | def wrapper(*args, **kwargs):
9 | future = coro(*args, **kwargs)
10 | return asyncio.get_event_loop().run_until_complete(future)
11 |
12 | return wrapper
13 |
--------------------------------------------------------------------------------
/tests/slack_sdk_async/http_retry/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/python-slack-sdk/443ca03e425d93f47822a4de5776ff0595f5ed3d/tests/slack_sdk_async/http_retry/__init__.py
--------------------------------------------------------------------------------
/tests/slack_sdk_async/http_retry/test_builtins.py:
--------------------------------------------------------------------------------
1 | import unittest
2 |
3 | from slack_sdk.http_retry.builtin_async_handlers import async_default_handlers
4 |
5 |
6 | class TestBuiltins(unittest.TestCase):
7 | def test_default_ones(self):
8 | list = async_default_handlers()
9 | self.assertEqual(1, len(list))
10 | list.clear()
11 | self.assertEqual(0, len(list))
12 | list = async_default_handlers()
13 | self.assertEqual(1, len(list))
14 |
--------------------------------------------------------------------------------
/tests/slack_sdk_async/my_retry_handler.py:
--------------------------------------------------------------------------------
1 | from typing import Optional
2 | from aiohttp import ServerDisconnectedError, ClientOSError
3 |
4 | from slack_sdk.http_retry.async_handler import AsyncRetryHandler
5 | from slack_sdk.http_retry.interval_calculator import RetryIntervalCalculator
6 | from slack_sdk.http_retry.state import RetryState
7 | from slack_sdk.http_retry.request import HttpRequest
8 | from slack_sdk.http_retry.response import HttpResponse
9 | from slack_sdk.http_retry.handler import default_interval_calculator
10 |
11 |
12 | class MyRetryHandler(AsyncRetryHandler):
13 | def __init__(
14 | self,
15 | max_retry_count: int = 1,
16 | interval_calculator: RetryIntervalCalculator = default_interval_calculator,
17 | ):
18 | super().__init__(max_retry_count, interval_calculator)
19 | self.call_count = 0
20 |
21 | async def _can_retry_async(
22 | self,
23 | *,
24 | state: RetryState,
25 | request: HttpRequest,
26 | response: Optional[HttpResponse],
27 | error: Optional[Exception],
28 | ) -> bool:
29 | self.call_count += 1
30 | if error is None:
31 | return False
32 | for error_type in [ServerDisconnectedError, ClientOSError]:
33 | if isinstance(error, error_type):
34 | return True
35 | return False
36 |
--------------------------------------------------------------------------------
/tests/slack_sdk_async/oauth/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/python-slack-sdk/443ca03e425d93f47822a4de5776ff0595f5ed3d/tests/slack_sdk_async/oauth/__init__.py
--------------------------------------------------------------------------------
/tests/slack_sdk_async/oauth/installation_store/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/python-slack-sdk/443ca03e425d93f47822a4de5776ff0595f5ed3d/tests/slack_sdk_async/oauth/installation_store/__init__.py
--------------------------------------------------------------------------------
/tests/slack_sdk_async/oauth/token_rotation/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/python-slack-sdk/443ca03e425d93f47822a4de5776ff0595f5ed3d/tests/slack_sdk_async/oauth/token_rotation/__init__.py
--------------------------------------------------------------------------------
/tests/slack_sdk_async/scim/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/python-slack-sdk/443ca03e425d93f47822a4de5776ff0595f5ed3d/tests/slack_sdk_async/scim/__init__.py
--------------------------------------------------------------------------------
/tests/slack_sdk_async/socket_mode/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/python-slack-sdk/443ca03e425d93f47822a4de5776ff0595f5ed3d/tests/slack_sdk_async/socket_mode/__init__.py
--------------------------------------------------------------------------------
/tests/slack_sdk_async/web/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/python-slack-sdk/443ca03e425d93f47822a4de5776ff0595f5ed3d/tests/slack_sdk_async/web/__init__.py
--------------------------------------------------------------------------------
/tests/slack_sdk_async/web/test_async_slack_response.py:
--------------------------------------------------------------------------------
1 | import unittest
2 |
3 | from slack_sdk.web.async_slack_response import AsyncSlackResponse
4 | from slack_sdk.web.async_client import AsyncWebClient
5 |
6 |
7 | class TestAsyncSlackResponse(unittest.TestCase):
8 | def setUp(self):
9 | pass
10 |
11 | def tearDown(self):
12 | pass
13 |
14 | # https://github.com/slackapi/python-slack-sdk/issues/1100
15 | def test_issue_1100(self):
16 | response = AsyncSlackResponse(
17 | client=AsyncWebClient(token="xoxb-dummy"),
18 | http_verb="POST",
19 | api_url="http://localhost:3000/api.test",
20 | req_args={},
21 | data=None,
22 | headers={},
23 | status_code=200,
24 | )
25 | with self.assertRaises(ValueError):
26 | response["foo"]
27 |
28 | foo = response.get("foo")
29 | self.assertIsNone(foo)
30 |
31 | # https://github.com/slackapi/python-slack-sdk/issues/1102
32 | def test_issue_1102(self):
33 | response = AsyncSlackResponse(
34 | client=AsyncWebClient(token="xoxb-dummy"),
35 | http_verb="POST",
36 | api_url="http://localhost:3000/api.test",
37 | req_args={},
38 | data={"ok": True, "args": {"hello": "world"}},
39 | headers={},
40 | status_code=200,
41 | )
42 | self.assertTrue("ok" in response)
43 | self.assertTrue("foo" not in response)
44 |
--------------------------------------------------------------------------------
/tests/slack_sdk_async/web/test_web_client_issue_829.py:
--------------------------------------------------------------------------------
1 | import unittest
2 |
3 | import slack_sdk.errors as err
4 | from slack_sdk.web.async_client import AsyncWebClient
5 | from tests.helpers import async_test
6 | from tests.slack_sdk.web.mock_web_api_handler import MockHandler
7 | from tests.mock_web_api_server import setup_mock_web_api_server_async, cleanup_mock_web_api_server_async
8 |
9 |
10 | class TestWebClient_Issue_829(unittest.TestCase):
11 | def setUp(self):
12 | setup_mock_web_api_server_async(self, MockHandler)
13 |
14 | def tearDown(self):
15 | cleanup_mock_web_api_server_async(self)
16 |
17 | @async_test
18 | async def test_html_response_body_issue_829_async(self):
19 | client = AsyncWebClient(base_url="http://localhost:8888")
20 | try:
21 | await client.users_list(token="xoxb-error_html_response")
22 | self.fail("SlackApiError expected here")
23 | except err.SlackApiError as e:
24 | self.assertEqual(
25 | "The request to the Slack API failed. (url: http://localhost:8888/users.list, status: 503)\n"
26 | "The server responded with: {}",
27 | str(e),
28 | )
29 |
--------------------------------------------------------------------------------
/tests/slack_sdk_async/web/test_web_client_issue_921_custom_logger.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | from logging import Logger
3 |
4 | from slack_sdk.web.async_client import AsyncWebClient
5 | from tests.helpers import async_test
6 | from tests.slack_sdk.web.mock_web_api_handler import MockHandler
7 | from tests.mock_web_api_server import setup_mock_web_api_server_async, cleanup_mock_web_api_server_async
8 |
9 |
10 | class TestWebClient_Issue_921_CustomLogger(unittest.TestCase):
11 | def setUp(self):
12 | setup_mock_web_api_server_async(self, MockHandler)
13 |
14 | def tearDown(self):
15 | cleanup_mock_web_api_server_async(self)
16 |
17 | @async_test
18 | async def test_if_it_uses_custom_logger(self):
19 | logger = CustomLogger("test-logger")
20 | client = AsyncWebClient(
21 | base_url="http://localhost:8888",
22 | token="xoxb-api_test",
23 | logger=logger,
24 | )
25 | await client.chat_postMessage(channel="C111", text="hello")
26 | self.assertTrue(logger.called)
27 |
28 |
29 | class CustomLogger(Logger):
30 | called: bool
31 |
32 | def __init__(self, name, level="DEBUG"):
33 | Logger.__init__(self, name, level)
34 | self.called = False
35 |
36 | def debug(self, msg, *args, **kwargs):
37 | self.called = True
38 |
--------------------------------------------------------------------------------
/tests/slack_sdk_async/webhook/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/python-slack-sdk/443ca03e425d93f47822a4de5776ff0595f5ed3d/tests/slack_sdk_async/webhook/__init__.py
--------------------------------------------------------------------------------
/tests/slack_sdk_fixture/channel.created.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "channel_created",
3 | "channel": {
4 | "id": "C024BE91L",
5 | "name": "fun",
6 | "created": 1360782804,
7 | "creator": "U024BE7LH"
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/tests/slack_sdk_fixture/im.created.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "im_created",
3 | "user": "U024BE7LH",
4 | "channel": {
5 | "id": "D024BE91L",
6 | "user": "U123BL234",
7 | "created": 1360782804
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/tests/slack_sdk_fixture/slack_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/python-slack-sdk/443ca03e425d93f47822a4de5776ff0595f5ed3d/tests/slack_sdk_fixture/slack_logo.png
--------------------------------------------------------------------------------
/tests/slack_sdk_fixture/slack_logo_new.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/python-slack-sdk/443ca03e425d93f47822a4de5776ff0595f5ed3d/tests/slack_sdk_fixture/slack_logo_new.png
--------------------------------------------------------------------------------
/tests/slack_sdk_fixture/view_home_006.json:
--------------------------------------------------------------------------------
1 | {
2 | "id": "VMHU10V25",
3 | "team_id": "T8N4K1JN",
4 | "type": "home",
5 | "blocks": [
6 | {
7 | "type": "section",
8 | "block_id": "2WGp9",
9 | "text": {
10 | "type": "mrkdwn",
11 | "text": "A simple section with some sample sentence.",
12 | "verbatim": false
13 | }
14 | }
15 | ],
16 | "private_metadata": "Shh it is a secret",
17 | "callback_id": "identify_your_home_tab",
18 | "hash": "156772938.1827394",
19 | "clear_on_close": false,
20 | "notify_on_close": false,
21 | "root_view_id": "VMHU10V25",
22 | "app_id": "AA4928AQ",
23 | "external_id": "some-unique-id",
24 | "bot_id": "BA13894H"
25 | }
--------------------------------------------------------------------------------
/tests/slack_sdk_fixture/view_modal_008.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "modal",
3 | "title": {
4 | "type": "plain_text",
5 | "text": "My App",
6 | "emoji": true
7 | },
8 | "submit": {
9 | "type": "plain_text",
10 | "text": "Submit",
11 | "emoji": true
12 | },
13 | "close": {
14 | "type": "plain_text",
15 | "text": "Cancel",
16 | "emoji": true
17 | },
18 | "private_metadata": "something important here",
19 | "blocks": [
20 | {
21 | "type": "section",
22 | "text": {
23 | "type": "mrkdwn",
24 | "text": "This is a mrkdwn section block :ghost: *this is bold*, and ~this is crossed out~, and "
25 | }
26 | }
27 | ],
28 | "state": {
29 | "values": {
30 | "multi-line": {
31 | "ml-value": {
32 | "type": "plain_text_input",
33 | "value": "This is my example inputted value"
34 | }
35 | }
36 | }
37 | },
38 | "hash": "156663117.cd33ad1f"
39 | }
--------------------------------------------------------------------------------
/tests/slack_sdk_fixture/view_modal_009.json:
--------------------------------------------------------------------------------
1 | {
2 | "id": "VNM522E2U",
3 | "team_id": "T9M4RL1JM",
4 | "type": "modal",
5 | "title": {
6 | "type": "plain_text",
7 | "text": "Pushed Modal",
8 | "emoji": true
9 | },
10 | "close": {
11 | "type": "plain_text",
12 | "text": "Back",
13 | "emoji": true
14 | },
15 | "submit": {
16 | "type": "plain_text",
17 | "text": "Save",
18 | "emoji": true
19 | },
20 | "blocks": [
21 | {
22 | "type": "input",
23 | "block_id": "edit_details",
24 | "element": {
25 | "type": "plain_text_input",
26 | "action_id": "detail_input"
27 | },
28 | "label": {
29 | "type": "plain_text",
30 | "text": "Edit details"
31 | }
32 | }
33 | ],
34 | "private_metadata": "secret",
35 | "callback_id": "view_4",
36 | "external_id": "some-unique-id",
37 | "state": {
38 | "values": {}
39 | },
40 | "hash": "1569362015.55b5e41b",
41 | "clear_on_close": true,
42 | "notify_on_close": false,
43 | "root_view_id": "VNN729E3U",
44 | "app_id": "AAD3351BQ",
45 | "bot_id": "BADF7A34H"
46 | }
--------------------------------------------------------------------------------
/tests/slack_sdk_fixture/view_modal_010.json:
--------------------------------------------------------------------------------
1 | {
2 | "id": "VMHU10V25",
3 | "team_id": "T8N4K1JN",
4 | "type": "modal",
5 | "title": {
6 | "type": "plain_text",
7 | "text": "Quite a plain modal"
8 | },
9 | "submit": {
10 | "type": "plain_text",
11 | "text": "Create"
12 | },
13 | "blocks": [
14 | {
15 | "type": "input",
16 | "block_id": "a_block_id",
17 | "label": {
18 | "type": "plain_text",
19 | "text": "A simple label",
20 | "emoji": true
21 | },
22 | "optional": false,
23 | "element": {
24 | "type": "plain_text_input",
25 | "action_id": "an_action_id"
26 | }
27 | }
28 | ],
29 | "private_metadata": "Shh it is a secret",
30 | "callback_id": "identify_your_modals",
31 | "external_id": "some-unique-id",
32 | "state": {
33 | "values": {}
34 | },
35 | "hash": "156772938.1827394",
36 | "clear_on_close": false,
37 | "notify_on_close": false,
38 | "root_view_id": "VMHU10V25",
39 | "app_id": "AA4928AQ",
40 | "bot_id": "BA13894H"
41 | }
--------------------------------------------------------------------------------
/tests/slack_sdk_fixture/web_response_admin_convo_pagination.json:
--------------------------------------------------------------------------------
1 | {
2 | "ok": true,
3 | "conversations": [
4 | {
5 | "id": "C013T0FTKU3",
6 | "name": "random",
7 | "purpose": "test",
8 | "member_count": 11,
9 | "created": 1589700571,
10 | "creator_id": "W111",
11 | "is_private": false,
12 | "is_archived": false,
13 | "is_general": false,
14 | "last_activity_ts": 1599130434000900,
15 | "is_ext_shared": false,
16 | "is_global_shared": false,
17 | "is_org_default": false,
18 | "is_org_mandatory": false,
19 | "is_org_shared": false,
20 | "is_frozen": false,
21 | "internal_team_ids_count": 1,
22 | "internal_team_ids_sample_team": "T111",
23 | "pending_connected_team_ids": [],
24 | "is_pending_ext_shared": false
25 | }
26 | ],
27 | "next_cursor": "1"
28 | }
29 |
--------------------------------------------------------------------------------
/tests/slack_sdk_fixture/web_response_admin_convo_pagination_1.json:
--------------------------------------------------------------------------------
1 | {
2 | "ok": true,
3 | "conversations": [],
4 | "next_cursor": ""
5 | }
6 |
--------------------------------------------------------------------------------
/tests/slack_sdk_fixture/web_response_api_test.json:
--------------------------------------------------------------------------------
1 | {
2 | "ok": true
3 | }
--------------------------------------------------------------------------------
/tests/slack_sdk_fixture/web_response_api_test_false.json:
--------------------------------------------------------------------------------
1 | {
2 | "ok": false
3 | }
--------------------------------------------------------------------------------
/tests/slack_sdk_fixture/web_response_conversations_list_pagination.json:
--------------------------------------------------------------------------------
1 | {
2 | "ok": true,
3 | "channels": [
4 | {
5 | "id": "C1"
6 | }
7 | ],
8 | "response_metadata": {
9 | "next_cursor": "has_page2"
10 | }
11 | }
--------------------------------------------------------------------------------
/tests/slack_sdk_fixture/web_response_conversations_list_pagination2.json:
--------------------------------------------------------------------------------
1 | {
2 | "ok": true,
3 | "channels": [
4 | {
5 | "id": "C2"
6 | }
7 | ],
8 | "response_metadata": {
9 | "next_cursor": "page2"
10 | }
11 | }
--------------------------------------------------------------------------------
/tests/slack_sdk_fixture/web_response_conversations_list_pagination2_page2.json:
--------------------------------------------------------------------------------
1 | {
2 | "ok": true,
3 | "channels": [
4 | {
5 | "id": "C3"
6 | }
7 | ]
8 | }
--------------------------------------------------------------------------------
/tests/slack_sdk_fixture/web_response_conversations_list_pagination_has_page2.json:
--------------------------------------------------------------------------------
1 | {
2 | "ok": true,
3 | "channels": [
4 | {
5 | "id": "C2"
6 | }
7 | ],
8 | "response_metadata": {
9 | "next_cursor": "has_page3"
10 | }
11 | }
--------------------------------------------------------------------------------
/tests/slack_sdk_fixture/web_response_conversations_list_pagination_has_page3.json:
--------------------------------------------------------------------------------
1 | {
2 | "ok": true,
3 | "channels": [
4 | {
5 | "id": "C3"
6 | }
7 | ]
8 | }
--------------------------------------------------------------------------------
/tests/slack_sdk_fixture/web_response_fatal_error_only_once.json:
--------------------------------------------------------------------------------
1 | {
2 | "ok": true
3 | }
--------------------------------------------------------------------------------
/tests/slack_sdk_fixture/web_response_rate_limited_only_once.json:
--------------------------------------------------------------------------------
1 | {
2 | "ok": true
3 | }
--------------------------------------------------------------------------------
/tests/slack_sdk_fixture/web_response_ratelimited_only_once.json:
--------------------------------------------------------------------------------
1 | {
2 | "ok": true
3 | }
--------------------------------------------------------------------------------
/tests/slack_sdk_fixture/web_response_users_list_pagination.json:
--------------------------------------------------------------------------------
1 | {
2 | "ok": true,
3 | "members": [
4 | "Bob",
5 | "cat"
6 | ],
7 | "response_metadata": {
8 | "next_cursor": 1
9 | }
10 | }
--------------------------------------------------------------------------------
/tests/slack_sdk_fixture/web_response_users_list_pagination_1.json:
--------------------------------------------------------------------------------
1 | {
2 | "ok": true,
3 | "members": [
4 | "Kevin",
5 | "dog"
6 | ],
7 | "response_metadata": {
8 | "next_cursor": ""
9 | }
10 | }
--------------------------------------------------------------------------------
/tests/slack_sdk_fixture/web_response_users_setPhoto.json:
--------------------------------------------------------------------------------
1 | {
2 | "ok": true
3 | }
--------------------------------------------------------------------------------
/tests/slack_sdk_fixture/日本語.txt:
--------------------------------------------------------------------------------
1 | 日本語の文書です。
--------------------------------------------------------------------------------
/tests/test_aiohttp_version_checker.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import unittest
3 |
4 | from slack_sdk.aiohttp_version_checker import validate_aiohttp_version
5 |
6 |
7 | class TestAiohttpVersionChecker(unittest.TestCase):
8 | def setUp(self):
9 | self.logger = logging.getLogger(__name__)
10 |
11 | def tearDown(self):
12 | pass
13 |
14 | def test_not_recommended_versions(self):
15 | state = {"counter": 0}
16 |
17 | def print(message: str):
18 | state["counter"] = state["counter"] + 1
19 |
20 | validate_aiohttp_version("2.1.3", print)
21 | self.assertEqual(state["counter"], 1)
22 | validate_aiohttp_version("3.6.3", print)
23 | self.assertEqual(state["counter"], 2)
24 | validate_aiohttp_version("3.7.0", print)
25 | self.assertEqual(state["counter"], 3)
26 |
27 | def test_recommended_versions(self):
28 | state = {"counter": 0}
29 |
30 | def print(message: str):
31 | state["counter"] = state["counter"] + 1
32 |
33 | validate_aiohttp_version("3.7.1", print)
34 | self.assertEqual(state["counter"], 0)
35 | validate_aiohttp_version("3.7.3", print)
36 | self.assertEqual(state["counter"], 0)
37 | validate_aiohttp_version("3.8.0", print)
38 | self.assertEqual(state["counter"], 0)
39 | validate_aiohttp_version("4.0.0", print)
40 | self.assertEqual(state["counter"], 0)
41 | validate_aiohttp_version("4.0.0rc1", print)
42 | self.assertEqual(state["counter"], 0)
43 |
--------------------------------------------------------------------------------
/tests/test_proxy_env_variable_loader.py:
--------------------------------------------------------------------------------
1 | import os
2 | import unittest
3 |
4 | from slack_sdk.proxy_env_variable_loader import load_http_proxy_from_env
5 | from tests.helpers import remove_os_env_temporarily, restore_os_env
6 |
7 |
8 | class TestProxyEnvVariableLoader(unittest.TestCase):
9 | def setUp(self):
10 | self.old_env = remove_os_env_temporarily()
11 |
12 | def tearDown(self):
13 | os.environ.clear()
14 | restore_os_env(self.old_env)
15 |
16 | def test_load_lower_case(self):
17 | os.environ["https_proxy"] = "http://localhost:9999"
18 | url = load_http_proxy_from_env()
19 | self.assertEqual(url, "http://localhost:9999")
20 |
21 | def test_load_upper_case(self):
22 | os.environ["HTTPS_PROXY"] = "http://localhost:9999"
23 | url = load_http_proxy_from_env()
24 | self.assertEqual(url, "http://localhost:9999")
25 |
26 | def test_load_all_empty_case(self):
27 | os.environ["HTTP_PROXY"] = ""
28 | os.environ["http_proxy"] = ""
29 | os.environ["HTTPS_PROXY"] = ""
30 | os.environ["https_proxy"] = ""
31 | url = load_http_proxy_from_env()
32 | self.assertEqual(url, None)
33 |
34 | def test_proxy_url_is_none_case(self):
35 | os.environ.pop("HTTPS_PROXY", None)
36 | os.environ.pop("https_proxy", None)
37 | os.environ.pop("HTTP_PROXY", None)
38 | os.environ.pop("http_proxy", None)
39 | url = load_http_proxy_from_env()
40 | self.assertEqual(url, None)
41 |
--------------------------------------------------------------------------------
/tests/web/classes/test_init.py:
--------------------------------------------------------------------------------
1 | import unittest
2 |
3 | from slack.web.classes import extract_json
4 | from slack.web.classes.objects import PlainTextObject, MarkdownTextObject
5 |
6 |
7 | class TestInit(unittest.TestCase):
8 | def test_from_list_of_json_objects(self):
9 | json_objects = [
10 | PlainTextObject.from_str("foo"),
11 | MarkdownTextObject.from_str("bar"),
12 | ]
13 | output = extract_json(json_objects)
14 | expected = {
15 | "result": [
16 | {"type": "plain_text", "text": "foo", "emoji": True},
17 | {"type": "mrkdwn", "text": "bar"},
18 | ]
19 | }
20 | self.assertDictEqual(expected, {"result": output})
21 |
22 | def test_from_single_json_object(self):
23 | single_json_object = PlainTextObject.from_str("foo")
24 | output = extract_json(single_json_object)
25 | expected = {"result": {"type": "plain_text", "text": "foo", "emoji": True}}
26 | self.assertDictEqual(expected, {"result": output})
27 |
--------------------------------------------------------------------------------
/tests/web/classes/test_messages.py:
--------------------------------------------------------------------------------
1 | import unittest
2 |
3 | from slack_sdk.models.messages.message import Message
4 |
5 |
6 | class MessageTests(unittest.TestCase):
7 | def test_validate_json_fails(self):
8 | msg = Message(text="Hi there")
9 | self.assertIsNotNone(msg)
10 |
--------------------------------------------------------------------------------
/tests/web/test_slack_response.py:
--------------------------------------------------------------------------------
1 | import unittest
2 |
3 | from slack import WebClient
4 | from slack.web.slack_response import SlackResponse
5 |
6 |
7 | class TestSlackResponse(unittest.TestCase):
8 | def setUp(self):
9 | pass
10 |
11 | def tearDown(self):
12 | pass
13 |
14 | # https://github.com/slackapi/python-slackclient/issues/559
15 | def test_issue_559(self):
16 | response = SlackResponse(
17 | client=WebClient(token="xoxb-dummy"),
18 | http_verb="POST",
19 | api_url="http://localhost:3000/api.test",
20 | req_args={},
21 | data={"ok": True, "args": {"hello": "world"}},
22 | headers={},
23 | status_code=200,
24 | )
25 |
26 | self.assertTrue("ok" in response.data)
27 | self.assertTrue("args" in response.data)
28 | self.assertFalse("error" in response.data)
29 |
--------------------------------------------------------------------------------
/tests/web/test_web_client_coverage.py:
--------------------------------------------------------------------------------
1 | # We no longer maintain this test.
2 | # Add new method tests to slack_sdk_tests_async/web/test_web_client_coverage.py
3 |
--------------------------------------------------------------------------------
/tests/web/test_web_client_functional.py:
--------------------------------------------------------------------------------
1 | import unittest
2 |
3 | import slack
4 | from tests.web.mock_web_api_handler import MockHandler
5 | from tests.mock_web_api_server import setup_mock_web_api_server, cleanup_mock_web_api_server
6 |
7 |
8 | class TestWebClientFunctional(unittest.TestCase):
9 | def setUp(self):
10 | setup_mock_web_api_server(self, MockHandler)
11 | self.client = slack.WebClient(token="xoxb-api_test", base_url="http://localhost:8888")
12 |
13 | def tearDown(self):
14 | cleanup_mock_web_api_server(self)
15 |
16 | def test_requests_with_use_session_turned_off(self):
17 | self.client.use_pooling = False
18 | resp = self.client.api_test()
19 | assert resp["ok"]
20 |
21 | def test_subsequent_requests_with_a_session_succeeds(self):
22 | resp = self.client.api_test()
23 | assert resp["ok"]
24 | resp = self.client.api_test()
25 | assert resp["ok"]
26 |
--------------------------------------------------------------------------------
/tests/web/test_web_client_issue_921_custom_logger.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | from logging import Logger
3 |
4 | from slack.web import WebClient
5 | from tests.slack_sdk.web.mock_web_api_handler import MockHandler
6 | from tests.mock_web_api_server import setup_mock_web_api_server, cleanup_mock_web_api_server
7 |
8 |
9 | class TestWebClient_Issue_921_CustomLogger(unittest.TestCase):
10 | def setUp(self):
11 | setup_mock_web_api_server(self, MockHandler)
12 |
13 | def tearDown(self):
14 | cleanup_mock_web_api_server(self)
15 |
16 | def test_if_it_uses_custom_logger(self):
17 | logger = CustomLogger("test-logger")
18 | client = WebClient(
19 | base_url="http://localhost:8888",
20 | token="xoxb-api_test",
21 | logger=logger,
22 | )
23 | client.chat_postMessage(channel="C111", text="hello")
24 | self.assertTrue(logger.called)
25 |
26 |
27 | class CustomLogger(Logger):
28 | called: bool
29 |
30 | def __init__(self, name, level="DEBUG"):
31 | Logger.__init__(self, name, level)
32 | self.called = False
33 |
34 | def debug(self, msg, *args, **kwargs):
35 | self.called = True
36 |
--------------------------------------------------------------------------------
/tutorial/PythOnBoardingBot/requirements.txt:
--------------------------------------------------------------------------------
1 | slack_sdk>=3.0
2 | slack_bolt>=1.6.1
3 | certifi
--------------------------------------------------------------------------------
/tutorial/assets/authorize-install.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/python-slack-sdk/443ca03e425d93f47822a4de5776ff0595f5ed3d/tutorial/assets/authorize-install.png
--------------------------------------------------------------------------------
/tutorial/assets/bot-token.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/python-slack-sdk/443ca03e425d93f47822a4de5776ff0595f5ed3d/tutorial/assets/bot-token.png
--------------------------------------------------------------------------------
/tutorial/assets/enable-events.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/python-slack-sdk/443ca03e425d93f47822a4de5776ff0595f5ed3d/tutorial/assets/enable-events.png
--------------------------------------------------------------------------------
/tutorial/assets/oauth-installation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/python-slack-sdk/443ca03e425d93f47822a4de5776ff0595f5ed3d/tutorial/assets/oauth-installation.png
--------------------------------------------------------------------------------
/tutorial/assets/oauth-permissions.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/python-slack-sdk/443ca03e425d93f47822a4de5776ff0595f5ed3d/tutorial/assets/oauth-permissions.png
--------------------------------------------------------------------------------
/tutorial/assets/signing-secret.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/python-slack-sdk/443ca03e425d93f47822a4de5776ff0595f5ed3d/tutorial/assets/signing-secret.png
--------------------------------------------------------------------------------
/tutorial/assets/subscribe-events.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/python-slack-sdk/443ca03e425d93f47822a4de5776ff0595f5ed3d/tutorial/assets/subscribe-events.png
--------------------------------------------------------------------------------