├── .flake8 ├── .git-blame-ignore-revs ├── .github ├── CODE_OF_CONDUCT.md ├── 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 │ ├── codecov.yml │ ├── docs-deploy.yml │ ├── flake8.yml │ ├── mypy.yml │ ├── tests.yml │ └── triage-issues.yml ├── .gitignore ├── LICENSE ├── MANIFEST.in ├── README.md ├── codecov.yml ├── docs ├── .gitignore ├── README.md ├── babel.config.js ├── content │ ├── concepts │ │ ├── acknowledge.md │ │ ├── actions.md │ │ ├── adapters.md │ │ ├── ai-apps.md │ │ ├── app-home.md │ │ ├── async.md │ │ ├── authenticating-oauth.md │ │ ├── authorization.md │ │ ├── commands.md │ │ ├── context.md │ │ ├── custom-adapters.md │ │ ├── custom-steps-dynamic-options.md │ │ ├── custom-steps.md │ │ ├── errors.md │ │ ├── event-listening.md │ │ ├── global-middleware.md │ │ ├── lazy-listeners.md │ │ ├── listener-middleware.md │ │ ├── logging.md │ │ ├── message-listening.md │ │ ├── message-sending.md │ │ ├── opening-modals.md │ │ ├── select-menu-options.md │ │ ├── shortcuts.md │ │ ├── socket-mode.md │ │ ├── steps-from-apps.md │ │ ├── token-rotation.md │ │ ├── updating-pushing-views.md │ │ ├── view-submissions.md │ │ └── web-api.md │ ├── getting-started.md │ ├── index.md │ └── tutorial │ │ ├── ai-chatbot.md │ │ ├── custom-steps-for-jira.md │ │ ├── custom-steps-workflow-builder-existing.md │ │ ├── custom-steps-workflow-builder-new.md │ │ ├── custom-steps.md │ │ └── modals.md ├── docusaurus.config.js ├── footerConfig.js ├── i18n │ └── ja-jp │ │ ├── README.md │ │ ├── code.json │ │ ├── docusaurus-plugin-content-docs │ │ ├── current.json │ │ └── current │ │ │ ├── concepts │ │ │ ├── acknowledge.md │ │ │ ├── actions.md │ │ │ ├── adapters.md │ │ │ ├── app-home.md │ │ │ ├── assistant.md │ │ │ ├── async.md │ │ │ ├── authenticating-oauth.md │ │ │ ├── authorization.md │ │ │ ├── commands.md │ │ │ ├── context.md │ │ │ ├── custom-adapters.md │ │ │ ├── custom-steps.md │ │ │ ├── errors.md │ │ │ ├── event-listening.md │ │ │ ├── global-middleware.md │ │ │ ├── lazy-listeners.md │ │ │ ├── listener-middleware.md │ │ │ ├── logging.md │ │ │ ├── message-listening.md │ │ │ ├── message-sending.md │ │ │ ├── opening-modals.md │ │ │ ├── select-menu-options.md │ │ │ ├── shortcuts.md │ │ │ ├── socket-mode.md │ │ │ ├── token-rotation.md │ │ │ ├── updating-pushing-views.md │ │ │ ├── view-submissions.md │ │ │ └── web-api.md │ │ │ ├── getting-started.md │ │ │ └── legacy │ │ │ └── steps-from-apps.md │ │ └── docusaurus-theme-classic │ │ ├── footer.json │ │ └── navbar.json ├── navbarConfig.js ├── package-lock.json ├── package.json ├── sidebars.js ├── src │ ├── css │ │ └── custom.css │ └── theme │ │ └── NotFound │ │ ├── Content │ │ └── index.js │ │ └── index.js └── static │ ├── .nojekyll │ ├── api-docs │ └── slack_bolt │ │ ├── adapter │ │ ├── aiohttp │ │ │ └── index.html │ │ ├── asgi │ │ │ ├── aiohttp │ │ │ │ └── index.html │ │ │ ├── async_handler.html │ │ │ ├── base_handler.html │ │ │ ├── builtin │ │ │ │ └── index.html │ │ │ ├── http_request.html │ │ │ ├── http_response.html │ │ │ ├── index.html │ │ │ └── utils.html │ │ ├── aws_lambda │ │ │ ├── chalice_handler.html │ │ │ ├── chalice_lazy_listener_runner.html │ │ │ ├── handler.html │ │ │ ├── index.html │ │ │ ├── internals.html │ │ │ ├── lambda_s3_oauth_flow.html │ │ │ ├── lazy_listener_runner.html │ │ │ └── local_lambda_client.html │ │ ├── bottle │ │ │ ├── handler.html │ │ │ └── index.html │ │ ├── cherrypy │ │ │ ├── handler.html │ │ │ └── index.html │ │ ├── django │ │ │ ├── handler.html │ │ │ └── index.html │ │ ├── falcon │ │ │ ├── async_resource.html │ │ │ ├── index.html │ │ │ └── resource.html │ │ ├── fastapi │ │ │ ├── async_handler.html │ │ │ └── index.html │ │ ├── flask │ │ │ ├── handler.html │ │ │ └── index.html │ │ ├── google_cloud_functions │ │ │ ├── handler.html │ │ │ └── index.html │ │ ├── index.html │ │ ├── pyramid │ │ │ ├── handler.html │ │ │ └── index.html │ │ ├── sanic │ │ │ ├── async_handler.html │ │ │ └── index.html │ │ ├── socket_mode │ │ │ ├── aiohttp │ │ │ │ └── index.html │ │ │ ├── async_base_handler.html │ │ │ ├── async_handler.html │ │ │ ├── async_internals.html │ │ │ ├── base_handler.html │ │ │ ├── builtin │ │ │ │ └── index.html │ │ │ ├── index.html │ │ │ ├── internals.html │ │ │ ├── websocket_client │ │ │ │ └── index.html │ │ │ └── websockets │ │ │ │ └── index.html │ │ ├── starlette │ │ │ ├── async_handler.html │ │ │ ├── handler.html │ │ │ └── index.html │ │ ├── tornado │ │ │ ├── async_handler.html │ │ │ ├── handler.html │ │ │ └── index.html │ │ └── wsgi │ │ │ ├── handler.html │ │ │ ├── http_request.html │ │ │ ├── http_response.html │ │ │ ├── index.html │ │ │ └── internals.html │ │ ├── app │ │ ├── app.html │ │ ├── async_app.html │ │ ├── async_server.html │ │ └── index.html │ │ ├── async_app.html │ │ ├── authorization │ │ ├── async_authorize.html │ │ ├── async_authorize_args.html │ │ ├── authorize.html │ │ ├── authorize_args.html │ │ ├── authorize_result.html │ │ └── index.html │ │ ├── context │ │ ├── ack │ │ │ ├── ack.html │ │ │ ├── async_ack.html │ │ │ ├── index.html │ │ │ └── internals.html │ │ ├── assistant │ │ │ ├── assistant_utilities.html │ │ │ ├── async_assistant_utilities.html │ │ │ ├── index.html │ │ │ ├── internals.html │ │ │ ├── thread_context │ │ │ │ └── index.html │ │ │ └── thread_context_store │ │ │ │ ├── async_store.html │ │ │ │ ├── default_async_store.html │ │ │ │ ├── default_store.html │ │ │ │ ├── file │ │ │ │ └── index.html │ │ │ │ ├── index.html │ │ │ │ └── store.html │ │ ├── async_context.html │ │ ├── base_context.html │ │ ├── complete │ │ │ ├── async_complete.html │ │ │ ├── complete.html │ │ │ └── index.html │ │ ├── context.html │ │ ├── fail │ │ │ ├── async_fail.html │ │ │ ├── fail.html │ │ │ └── index.html │ │ ├── get_thread_context │ │ │ ├── async_get_thread_context.html │ │ │ ├── get_thread_context.html │ │ │ └── index.html │ │ ├── index.html │ │ ├── respond │ │ │ ├── async_respond.html │ │ │ ├── index.html │ │ │ ├── internals.html │ │ │ └── respond.html │ │ ├── save_thread_context │ │ │ ├── async_save_thread_context.html │ │ │ ├── index.html │ │ │ └── save_thread_context.html │ │ ├── say │ │ │ ├── async_say.html │ │ │ ├── index.html │ │ │ ├── internals.html │ │ │ └── say.html │ │ ├── set_status │ │ │ ├── async_set_status.html │ │ │ ├── index.html │ │ │ └── set_status.html │ │ ├── set_suggested_prompts │ │ │ ├── async_set_suggested_prompts.html │ │ │ ├── index.html │ │ │ └── set_suggested_prompts.html │ │ └── set_title │ │ │ ├── async_set_title.html │ │ │ ├── index.html │ │ │ └── set_title.html │ │ ├── error │ │ └── index.html │ │ ├── index.html │ │ ├── kwargs_injection │ │ ├── args.html │ │ ├── async_args.html │ │ ├── async_utils.html │ │ ├── index.html │ │ └── utils.html │ │ ├── lazy_listener │ │ ├── async_internals.html │ │ ├── async_runner.html │ │ ├── asyncio_runner.html │ │ ├── index.html │ │ ├── internals.html │ │ ├── runner.html │ │ └── thread_runner.html │ │ ├── listener │ │ ├── async_builtins.html │ │ ├── async_listener.html │ │ ├── async_listener_completion_handler.html │ │ ├── async_listener_error_handler.html │ │ ├── async_listener_start_handler.html │ │ ├── asyncio_runner.html │ │ ├── builtins.html │ │ ├── custom_listener.html │ │ ├── index.html │ │ ├── listener.html │ │ ├── listener_completion_handler.html │ │ ├── listener_error_handler.html │ │ ├── listener_start_handler.html │ │ └── thread_runner.html │ │ ├── listener_matcher │ │ ├── async_builtins.html │ │ ├── async_listener_matcher.html │ │ ├── builtins.html │ │ ├── custom_listener_matcher.html │ │ ├── index.html │ │ └── listener_matcher.html │ │ ├── logger │ │ ├── index.html │ │ └── messages.html │ │ ├── middleware │ │ ├── assistant │ │ │ ├── assistant.html │ │ │ ├── async_assistant.html │ │ │ └── index.html │ │ ├── async_builtins.html │ │ ├── async_custom_middleware.html │ │ ├── async_middleware.html │ │ ├── async_middleware_error_handler.html │ │ ├── attaching_function_token │ │ │ ├── async_attaching_function_token.html │ │ │ ├── attaching_function_token.html │ │ │ └── index.html │ │ ├── authorization │ │ │ ├── async_authorization.html │ │ │ ├── async_internals.html │ │ │ ├── async_multi_teams_authorization.html │ │ │ ├── async_single_team_authorization.html │ │ │ ├── authorization.html │ │ │ ├── index.html │ │ │ ├── internals.html │ │ │ ├── multi_teams_authorization.html │ │ │ └── single_team_authorization.html │ │ ├── custom_middleware.html │ │ ├── ignoring_self_events │ │ │ ├── async_ignoring_self_events.html │ │ │ ├── ignoring_self_events.html │ │ │ └── index.html │ │ ├── index.html │ │ ├── message_listener_matches │ │ │ ├── async_message_listener_matches.html │ │ │ ├── index.html │ │ │ └── message_listener_matches.html │ │ ├── middleware.html │ │ ├── middleware_error_handler.html │ │ ├── request_verification │ │ │ ├── async_request_verification.html │ │ │ ├── index.html │ │ │ └── request_verification.html │ │ ├── ssl_check │ │ │ ├── async_ssl_check.html │ │ │ ├── index.html │ │ │ └── ssl_check.html │ │ └── url_verification │ │ │ ├── async_url_verification.html │ │ │ ├── index.html │ │ │ └── url_verification.html │ │ ├── oauth │ │ ├── async_callback_options.html │ │ ├── async_internals.html │ │ ├── async_oauth_flow.html │ │ ├── async_oauth_settings.html │ │ ├── callback_options.html │ │ ├── index.html │ │ ├── internals.html │ │ ├── oauth_flow.html │ │ └── oauth_settings.html │ │ ├── request │ │ ├── async_internals.html │ │ ├── async_request.html │ │ ├── index.html │ │ ├── internals.html │ │ ├── payload_utils.html │ │ └── request.html │ │ ├── response │ │ ├── index.html │ │ └── response.html │ │ ├── util │ │ ├── async_utils.html │ │ ├── index.html │ │ └── utils.html │ │ ├── version.html │ │ └── workflows │ │ ├── index.html │ │ └── step │ │ ├── async_step.html │ │ ├── async_step_middleware.html │ │ ├── index.html │ │ ├── internals.html │ │ ├── step.html │ │ ├── step_middleware.html │ │ └── utilities │ │ ├── async_complete.html │ │ ├── async_configure.html │ │ ├── async_fail.html │ │ ├── async_update.html │ │ ├── complete.html │ │ ├── configure.html │ │ ├── fail.html │ │ ├── index.html │ │ └── update.html │ └── img │ ├── bolt-logo.svg │ ├── bolt-py-logo.svg │ ├── boltpy │ ├── basic-information-page.png │ ├── bolt-favicon.png │ ├── bot-token.png │ ├── ngrok.gif │ ├── request-url-config.png │ └── signing-secret.png │ ├── favicon.ico │ ├── slack-logo-on-white.png │ ├── slack-logo.svg │ └── tutorials │ ├── ai-chatbot │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ ├── 6.png │ ├── 7.png │ └── 8.png │ ├── custom-steps-jira │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ ├── 6.png │ └── 7.png │ ├── custom-steps-wfb-existing │ ├── add-step.png │ ├── app-message.png │ ├── define-step.png │ ├── find-step.png │ ├── inputs.png │ ├── org-ready.png │ ├── outputs.png │ └── step-inputs.png │ ├── custom-steps-wfb-new │ ├── app-token.png │ ├── bot-token.png │ ├── install.png │ ├── manifest.png │ ├── wfb-1.png │ ├── wfb-10.png │ ├── wfb-11.png │ ├── wfb-12.png │ ├── wfb-2.png │ ├── wfb-3.png │ ├── wfb-4.png │ ├── wfb-5.png │ ├── wfb-6.png │ ├── wfb-7.png │ ├── wfb-8.png │ ├── wfb-9.png │ └── workflow-step.png │ └── modals │ ├── base_link.gif │ ├── final_product.gif │ ├── heart_icon.gif │ ├── interactivity_url.png │ └── slash_command.png ├── examples ├── aiohttp_devtools │ ├── async_app.py │ └── requirements.txt ├── app.py ├── app_authorize.py ├── asgi │ ├── app.py │ ├── async_app.py │ ├── async_oauth_app.py │ ├── oauth_app.py │ └── requirements.txt ├── assistants │ ├── app.py │ ├── async_app.py │ ├── async_interaction_app.py │ └── interaction_app.py ├── async_app.py ├── async_app_authorize.py ├── aws_chalice │ ├── .chalice │ │ ├── config.json.oauth │ │ └── config.json.simple │ ├── .gitignore │ ├── app.py │ ├── deploy.sh │ ├── oauth_app.py │ ├── requirements.txt │ └── simple_app.py ├── aws_lambda │ ├── .env.oauth_sample │ ├── .env.sample │ ├── .gitignore │ ├── README.md │ ├── aws_lambda.py │ ├── aws_lambda_config.yaml │ ├── aws_lambda_oauth.py │ ├── aws_lambda_oauth_config.yaml │ ├── deploy.sh │ ├── deploy_lazy.sh │ ├── deploy_oauth.sh │ ├── lazy_aws_lambda.py │ ├── lazy_aws_lambda_config.yaml │ ├── requirements.txt │ └── requirements_oauth.txt ├── bottle │ ├── app.py │ ├── oauth_app.py │ └── requirements.txt ├── cherrypy │ ├── app.py │ ├── oauth_app.py │ └── requirements.txt ├── dialogs_app.py ├── django │ ├── .gitignore │ ├── README.md │ ├── manage.py │ ├── myslackapp │ │ ├── __init__.py │ │ ├── asgi.py │ │ ├── settings.py │ │ ├── urls.py │ │ └── wsgi.py │ ├── mysql-docker-compose.yml │ ├── oauth_app │ │ ├── __init__.py │ │ ├── apps.py │ │ ├── migrations │ │ │ ├── 0001_initial.py │ │ │ ├── 0002_token_rotation.py │ │ │ └── __init__.py │ │ ├── models.py │ │ ├── slack_datastores.py │ │ ├── slack_listeners.py │ │ └── urls.py │ ├── requirements.txt │ └── simple_app │ │ ├── __init__.py │ │ ├── apps.py │ │ ├── migrations │ │ └── __init__.py │ │ ├── models.py │ │ ├── slack_listeners.py │ │ └── urls.py ├── docker │ ├── aiohttp │ │ ├── Dockerfile │ │ ├── main.py │ │ └── requirements.txt │ ├── asgi │ │ ├── Dockerfile │ │ ├── main.py │ │ └── requirements.txt │ ├── fastapi-gunicorn │ │ ├── Dockerfile │ │ ├── main.py │ │ └── requirements.txt │ ├── flask-gunicorn │ │ ├── Dockerfile │ │ ├── main.py │ │ └── requirements.txt │ ├── flask-uwsgi │ │ ├── Dockerfile │ │ ├── main.py │ │ ├── requirements.txt │ │ └── uwsgi.ini │ └── sanic │ │ ├── Dockerfile │ │ ├── main.py │ │ └── requirements.txt ├── events_app.py ├── falcon │ ├── app.py │ ├── async_app.py │ ├── async_oauth_app.py │ ├── oauth_app.py │ └── requirements.txt ├── fastapi │ ├── app.py │ ├── async_app.py │ ├── async_app_custom_props.py │ ├── async_oauth_app.py │ ├── oauth_app.py │ └── requirements.txt ├── flask │ ├── app.py │ ├── oauth_app.py │ └── requirements.txt ├── getting_started │ ├── .gitignore │ ├── README.md │ ├── app.py │ └── requirements.txt ├── google_app_engine │ └── flask │ │ ├── .gcloudignore │ │ ├── .gitignore │ │ ├── app.yaml │ │ ├── env_variables.yaml.sample │ │ ├── main.py │ │ └── requirements.txt ├── google_cloud_functions │ ├── .env.yaml.oauth-sample │ ├── .env.yaml.sample │ ├── .gcloudignore │ ├── .gitignore │ ├── datastore.py │ ├── oauth_main.py │ ├── requirements.txt │ └── simple_main.py ├── google_cloud_run │ ├── aiohttp │ │ ├── Dockerfile │ │ ├── main.py │ │ └── requirements.txt │ ├── flask-gunicorn │ │ ├── Dockerfile │ │ ├── main.py │ │ └── requirements.txt │ └── sanic │ │ ├── Dockerfile │ │ ├── main.py │ │ └── requirements.txt ├── heroku │ ├── Procfile │ ├── main.py │ └── requirements.txt ├── lazy_async_modals_app.py ├── lazy_modals_app.py ├── message_events.py ├── modals_app.py ├── modals_app_typed.py ├── oauth_app.py ├── oauth_app_settings.py ├── oauth_sqlite3_app.py ├── oauth_sqlite3_app_bot_only.py ├── oauth_sqlite3_app_org_level.py ├── pyramid │ ├── app.py │ ├── oauth_app.py │ └── requirements.txt ├── readme_app.py ├── readme_async_app.py ├── sanic │ ├── async_app.py │ ├── async_oauth_app.py │ └── requirements.txt ├── socket_mode.py ├── socket_mode_async.py ├── socket_mode_async_healthcheck.py ├── socket_mode_healthcheck.py ├── socket_mode_oauth.py ├── socket_mode_proxy.py ├── sqlalchemy │ ├── async_oauth_app.py │ ├── oauth_app.py │ ├── requirements.txt │ └── requirements_async.txt ├── starlette │ ├── app.py │ ├── async_app.py │ ├── async_oauth_app.py │ ├── oauth_app.py │ └── requirements.txt ├── tornado │ ├── app.py │ ├── async_app.py │ ├── async_oauth_app.py │ ├── oauth_app.py │ └── requirements.txt ├── workflow_steps │ ├── async_steps_from_apps.py │ ├── async_steps_from_apps_decorator.py │ ├── async_steps_from_apps_primitive.py │ ├── steps_from_apps.py │ ├── steps_from_apps_decorator.py │ └── steps_from_apps_primitive.py └── wsgi │ ├── app.py │ ├── oauth_app.py │ └── requirements.txt ├── logs └── .gitkeep ├── pyproject.toml ├── requirements.txt ├── requirements ├── adapter.txt ├── adapter_testing.txt ├── async.txt ├── testing.txt ├── testing_without_asyncio.txt └── tools.txt ├── scripts ├── build_pypi_package.sh ├── deploy_to_pypi_org.sh ├── deploy_to_test_pypi_org.sh ├── generate_api_docs.sh ├── install_all_and_run_tests.sh ├── run_flake8.sh ├── run_mypy.sh ├── run_tests.sh └── uninstall_all.sh ├── setup.cfg ├── slack_bolt ├── __init__.py ├── adapter │ ├── __init__.py │ ├── aiohttp │ │ └── __init__.py │ ├── asgi │ │ ├── __init__.py │ │ ├── aiohttp │ │ │ └── __init__.py │ │ ├── async_handler.py │ │ ├── base_handler.py │ │ ├── builtin │ │ │ └── __init__.py │ │ ├── http_request.py │ │ ├── http_response.py │ │ └── utils.py │ ├── aws_lambda │ │ ├── __init__.py │ │ ├── chalice_handler.py │ │ ├── chalice_lazy_listener_runner.py │ │ ├── handler.py │ │ ├── internals.py │ │ ├── lambda_s3_oauth_flow.py │ │ ├── lazy_listener_runner.py │ │ └── local_lambda_client.py │ ├── bottle │ │ ├── __init__.py │ │ └── handler.py │ ├── cherrypy │ │ ├── __init__.py │ │ └── handler.py │ ├── django │ │ ├── __init__.py │ │ └── handler.py │ ├── falcon │ │ ├── __init__.py │ │ ├── async_resource.py │ │ └── resource.py │ ├── fastapi │ │ ├── __init__.py │ │ └── async_handler.py │ ├── flask │ │ ├── __init__.py │ │ └── handler.py │ ├── google_cloud_functions │ │ ├── __init__.py │ │ └── handler.py │ ├── pyramid │ │ ├── __init__.py │ │ └── handler.py │ ├── sanic │ │ ├── __init__.py │ │ └── async_handler.py │ ├── socket_mode │ │ ├── __init__.py │ │ ├── aiohttp │ │ │ └── __init__.py │ │ ├── async_base_handler.py │ │ ├── async_handler.py │ │ ├── async_internals.py │ │ ├── base_handler.py │ │ ├── builtin │ │ │ └── __init__.py │ │ ├── internals.py │ │ ├── websocket_client │ │ │ └── __init__.py │ │ └── websockets │ │ │ └── __init__.py │ ├── starlette │ │ ├── __init__.py │ │ ├── async_handler.py │ │ └── handler.py │ ├── tornado │ │ ├── __init__.py │ │ ├── async_handler.py │ │ └── handler.py │ └── wsgi │ │ ├── __init__.py │ │ ├── handler.py │ │ ├── http_request.py │ │ ├── http_response.py │ │ └── internals.py ├── app │ ├── __init__.py │ ├── app.py │ ├── async_app.py │ └── async_server.py ├── async_app.py ├── authorization │ ├── __init__.py │ ├── async_authorize.py │ ├── async_authorize_args.py │ ├── authorize.py │ ├── authorize_args.py │ └── authorize_result.py ├── context │ ├── __init__.py │ ├── ack │ │ ├── __init__.py │ │ ├── ack.py │ │ ├── async_ack.py │ │ └── internals.py │ ├── assistant │ │ ├── __init__.py │ │ ├── assistant_utilities.py │ │ ├── async_assistant_utilities.py │ │ ├── internals.py │ │ ├── thread_context │ │ │ └── __init__.py │ │ └── thread_context_store │ │ │ ├── __init__.py │ │ │ ├── async_store.py │ │ │ ├── default_async_store.py │ │ │ ├── default_store.py │ │ │ ├── file │ │ │ └── __init__.py │ │ │ └── store.py │ ├── async_context.py │ ├── base_context.py │ ├── complete │ │ ├── __init__.py │ │ ├── async_complete.py │ │ └── complete.py │ ├── context.py │ ├── fail │ │ ├── __init__.py │ │ ├── async_fail.py │ │ └── fail.py │ ├── get_thread_context │ │ ├── __init__.py │ │ ├── async_get_thread_context.py │ │ └── get_thread_context.py │ ├── respond │ │ ├── __init__.py │ │ ├── async_respond.py │ │ ├── internals.py │ │ └── respond.py │ ├── save_thread_context │ │ ├── __init__.py │ │ ├── async_save_thread_context.py │ │ └── save_thread_context.py │ ├── say │ │ ├── __init__.py │ │ ├── async_say.py │ │ ├── internals.py │ │ └── say.py │ ├── set_status │ │ ├── __init__.py │ │ ├── async_set_status.py │ │ └── set_status.py │ ├── set_suggested_prompts │ │ ├── __init__.py │ │ ├── async_set_suggested_prompts.py │ │ └── set_suggested_prompts.py │ └── set_title │ │ ├── __init__.py │ │ ├── async_set_title.py │ │ └── set_title.py ├── error │ └── __init__.py ├── kwargs_injection │ ├── __init__.py │ ├── args.py │ ├── async_args.py │ ├── async_utils.py │ └── utils.py ├── lazy_listener │ ├── __init__.py │ ├── async_internals.py │ ├── async_runner.py │ ├── asyncio_runner.py │ ├── internals.py │ ├── runner.py │ └── thread_runner.py ├── listener │ ├── __init__.py │ ├── async_builtins.py │ ├── async_listener.py │ ├── async_listener_completion_handler.py │ ├── async_listener_error_handler.py │ ├── async_listener_start_handler.py │ ├── asyncio_runner.py │ ├── builtins.py │ ├── custom_listener.py │ ├── listener.py │ ├── listener_completion_handler.py │ ├── listener_error_handler.py │ ├── listener_start_handler.py │ └── thread_runner.py ├── listener_matcher │ ├── __init__.py │ ├── async_builtins.py │ ├── async_listener_matcher.py │ ├── builtins.py │ ├── custom_listener_matcher.py │ └── listener_matcher.py ├── logger │ ├── __init__.py │ └── messages.py ├── middleware │ ├── __init__.py │ ├── assistant │ │ ├── __init__.py │ │ ├── assistant.py │ │ └── async_assistant.py │ ├── async_builtins.py │ ├── async_custom_middleware.py │ ├── async_middleware.py │ ├── async_middleware_error_handler.py │ ├── attaching_function_token │ │ ├── __init__.py │ │ ├── async_attaching_function_token.py │ │ └── attaching_function_token.py │ ├── authorization │ │ ├── __init__.py │ │ ├── async_authorization.py │ │ ├── async_internals.py │ │ ├── async_multi_teams_authorization.py │ │ ├── async_single_team_authorization.py │ │ ├── authorization.py │ │ ├── internals.py │ │ ├── multi_teams_authorization.py │ │ └── single_team_authorization.py │ ├── custom_middleware.py │ ├── ignoring_self_events │ │ ├── __init__.py │ │ ├── async_ignoring_self_events.py │ │ └── ignoring_self_events.py │ ├── message_listener_matches │ │ ├── __init__.py │ │ ├── async_message_listener_matches.py │ │ └── message_listener_matches.py │ ├── middleware.py │ ├── middleware_error_handler.py │ ├── request_verification │ │ ├── __init__.py │ │ ├── async_request_verification.py │ │ └── request_verification.py │ ├── ssl_check │ │ ├── __init__.py │ │ ├── async_ssl_check.py │ │ └── ssl_check.py │ └── url_verification │ │ ├── __init__.py │ │ ├── async_url_verification.py │ │ └── url_verification.py ├── oauth │ ├── __init__.py │ ├── async_callback_options.py │ ├── async_internals.py │ ├── async_oauth_flow.py │ ├── async_oauth_settings.py │ ├── callback_options.py │ ├── internals.py │ ├── oauth_flow.py │ └── oauth_settings.py ├── py.typed ├── request │ ├── __init__.py │ ├── async_internals.py │ ├── async_request.py │ ├── internals.py │ ├── payload_utils.py │ └── request.py ├── response │ ├── __init__.py │ └── response.py ├── util │ ├── __init__.py │ ├── async_utils.py │ └── utils.py ├── version.py └── workflows │ ├── __init__.py │ └── step │ ├── __init__.py │ ├── async_step.py │ ├── async_step_middleware.py │ ├── internals.py │ ├── step.py │ ├── step_middleware.py │ └── utilities │ ├── __init__.py │ ├── async_complete.py │ ├── async_configure.py │ ├── async_fail.py │ ├── async_update.py │ ├── complete.py │ ├── configure.py │ ├── fail.py │ └── update.py └── tests ├── __init__.py ├── adapter_tests ├── __init__.py ├── asgi │ ├── __init__.py │ ├── test_asgi_http.py │ └── test_asgi_lifespan.py ├── aws │ ├── __init__.py │ ├── test_aws_chalice.py │ ├── test_aws_lambda.py │ └── test_lambda_s3_oauth_flow.py ├── bottle │ ├── __init__.py │ ├── test_bottle.py │ └── test_bottle_oauth.py ├── cherrypy │ ├── __init__.py │ ├── test_cherrypy.py │ └── test_cherrypy_oauth.py ├── django │ ├── __init__.py │ ├── test_django.py │ └── test_django_settings.py ├── falcon │ ├── __init__.py │ └── test_falcon.py ├── flask │ ├── __init__.py │ └── test_flask.py ├── google_cloud_functions │ ├── __init__.py │ └── test_google_cloud_functions.py ├── pyramid │ ├── __init__.py │ └── test_pyramid.py ├── socket_mode │ ├── __init__.py │ ├── mock_socket_mode_server.py │ ├── mock_web_api_server.py │ ├── test_interactions_builtin.py │ ├── test_interactions_websocket_client.py │ └── test_lazy_listeners.py ├── starlette │ ├── __init__.py │ ├── test_fastapi.py │ └── test_starlette.py ├── tornado │ ├── __init__.py │ ├── test_tornado.py │ └── test_tornado_oauth.py └── wsgi │ ├── __init__.py │ └── test_wsgi_http.py ├── adapter_tests_async ├── __init__.py ├── socket_mode │ ├── __init__.py │ ├── test_async_aiohttp.py │ ├── test_async_lazy_listeners.py │ └── test_async_websockets.py ├── test_async_asgi.py ├── test_async_falcon.py ├── test_async_fastapi.py ├── test_async_sanic.py ├── test_async_starlette.py ├── test_tornado.py └── test_tornado_oauth.py ├── mock_asgi_server.py ├── mock_web_api_server ├── __init__.py ├── mock_handler.py ├── mock_server_thread.py └── received_requests.py ├── mock_wsgi_server.py ├── scenario_tests ├── __init__.py ├── test_app.py ├── test_app_actor_user_token.py ├── test_app_bot_only.py ├── test_app_custom_authorize.py ├── test_app_decorators.py ├── test_app_installation_store.py ├── test_app_using_methods_in_class.py ├── test_attachment_actions.py ├── test_authorize.py ├── test_block_actions.py ├── test_block_actions_respond.py ├── test_block_suggestion.py ├── test_dialogs.py ├── test_error_handler.py ├── test_events.py ├── test_events_assistant.py ├── test_events_ignore_self.py ├── test_events_org_apps.py ├── test_events_request_verification.py ├── test_events_shared_channels.py ├── test_events_socket_mode.py ├── test_events_token_revocations.py ├── test_events_url_verification.py ├── test_function.py ├── test_installation_store_authorize.py ├── test_lazy.py ├── test_listener_middleware.py ├── test_message.py ├── test_message_bot.py ├── test_message_changed.py ├── test_message_deleted.py ├── test_message_file_share.py ├── test_message_thread_broadcast.py ├── test_middleware.py ├── test_shortcut.py ├── test_slash_command.py ├── test_ssl_check.py ├── test_view_closed.py ├── test_view_submission.py ├── test_web_client_customization.py ├── test_workflow_steps.py ├── test_workflow_steps_decorator_simple.py └── test_workflow_steps_decorator_with_args.py ├── scenario_tests_async ├── __init__.py ├── test_app.py ├── test_app_actor_user_token.py ├── test_app_bot_only.py ├── test_app_custom_authorize.py ├── test_app_decorators.py ├── test_app_dispatch.py ├── test_app_installation_store.py ├── test_app_using_methods_in_class.py ├── test_attachment_actions.py ├── test_authorize.py ├── test_block_actions.py ├── test_block_actions_respond.py ├── test_block_suggestion.py ├── test_dialogs.py ├── test_error_handler.py ├── test_events.py ├── test_events_assistant.py ├── test_events_ignore_self.py ├── test_events_org_apps.py ├── test_events_request_verification.py ├── test_events_shared_channels.py ├── test_events_socket_mode.py ├── test_events_token_revocations.py ├── test_events_url_verification.py ├── test_function.py ├── test_installation_store_authorize.py ├── test_lazy.py ├── test_listener_middleware.py ├── test_message.py ├── test_message_bot.py ├── test_message_changed.py ├── test_message_deleted.py ├── test_message_file_share.py ├── test_message_thread_broadcast.py ├── test_middleware.py ├── test_shortcut.py ├── test_slash_command.py ├── test_ssl_check.py ├── test_view_closed.py ├── test_view_submission.py ├── test_web_client_customization.py ├── test_workflow_steps.py ├── test_workflow_steps_decorator_simple.py └── test_workflow_steps_decorator_with_args.py ├── slack_bolt ├── __init__.py ├── app │ ├── __init__.py │ └── test_dev_server.py ├── authorization │ ├── __init__.py │ └── test_authorize.py ├── context │ ├── __init__.py │ ├── test_ack.py │ ├── test_complete.py │ ├── test_fail.py │ ├── test_respond.py │ ├── test_respond_internals.py │ └── test_say.py ├── error │ ├── __init__.py │ └── test_errors.py ├── kwargs_injection │ ├── __init__.py │ └── test_args.py ├── listener │ ├── __init__.py │ ├── test_listener_completion_handler.py │ └── test_listener_start_handler.py ├── listener_matcher │ ├── __init__.py │ ├── test_builtins.py │ └── test_custom_listener_matcher.py ├── logger │ ├── __init__.py │ └── test_unmatched_suggestions.py ├── middleware │ ├── __init__.py │ ├── authorization │ │ ├── __init__.py │ │ └── test_single_team_authorization.py │ └── request_verification │ │ ├── __init__.py │ │ └── test_request_verification.py ├── oauth │ ├── __init__.py │ ├── test_internals.py │ ├── test_oauth_flow.py │ └── test_oauth_flow_sqlite3.py ├── request │ ├── __init__.py │ ├── test_internals.py │ └── test_request.py ├── util │ ├── __init__.py │ └── test_util.py └── workflows │ ├── __init__.py │ └── step │ ├── __init__.py │ └── test_step.py ├── slack_bolt_async ├── __init__.py ├── app │ ├── __init__.py │ └── test_server.py ├── authorization │ ├── __init__.py │ └── test_async_authorize.py ├── context │ ├── __init__.py │ ├── test_async_ack.py │ ├── test_async_complete.py │ ├── test_async_fail.py │ ├── test_async_respond.py │ └── test_async_say.py ├── kwargs_injection │ ├── __init__.py │ └── test_async_args.py ├── listener │ ├── __init__.py │ ├── test_async_listener_completion_handler.py │ └── test_async_listener_start_handler.py ├── listener_matcher │ ├── __init__.py │ └── test_async_custom_listener_matcher.py ├── logger │ ├── __init__.py │ └── test_unmatched_suggestions.py ├── middleware │ ├── __init__.py │ ├── authorization │ │ ├── __init__.py │ │ └── test_single_team_authorization.py │ └── request_verification │ │ ├── __init__.py │ │ └── test_request_verification.py ├── oauth │ ├── __init__.py │ ├── test_async_oauth_flow.py │ └── test_async_oauth_flow_sqlite3.py ├── request │ ├── __init__.py │ └── test_async_request.py └── workflows │ ├── __init__.py │ └── step │ ├── __init__.py │ └── test_async_step.py └── utils.py /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | max-line-length = 125 3 | ignore = F841,F821,W503,E402 4 | -------------------------------------------------------------------------------- /.git-blame-ignore-revs: -------------------------------------------------------------------------------- 1 | # change black settings 2 | 0e4cd56b69e8f83166cd262f762802b7f18c3d21 3 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | ## Introduction 4 | 5 | Diversity and inclusion make our community strong. We encourage participation from the most varied and diverse backgrounds possible and want to be very clear about where we stand. 6 | 7 | Our goal is to maintain a safe, helpful and friendly community for everyone, regardless of experience, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, nationality, or other defining characteristic. 8 | 9 | This code and related procedures also apply to unacceptable behavior occurring outside the scope of community activities, in all community venues (online and in-person) as well as in all one-on-one communications, and anywhere such behavior has the potential to adversely affect the safety and well-being of community members. 10 | 11 | For more information on our code of conduct, please visit [https://slackhq.github.io/code-of-conduct](https://slackhq.github.io/code-of-conduct) -------------------------------------------------------------------------------- /.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_bolt.App** and/or its core components 14 | * [ ] **slack_bolt.async_app.AsyncApp** and/or its core components 15 | * [ ] Adapters in **slack_bolt.adapter** 16 | * [ ] Others 17 | 18 | ## Requirements 19 | 20 | Please read the [Contributing guidelines](https://github.com/slackapi/bolt-python/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. 21 | -------------------------------------------------------------------------------- /.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/bolt-python/ 14 | 15 | ## Requirements 16 | 17 | Please read the [Contributing guidelines](https://github.com/slackapi/bolt-python/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/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 | 8 | 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. 9 | -------------------------------------------------------------------------------- /.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_bolt` 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., setup.py)) 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 | Please read the [Contributing guidelines](https://github.com/slackapi/bolt-python/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. 42 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Summary 2 | 3 | 4 | 5 | ### Testing 6 | 7 | 8 | 9 | ### Category 10 | 11 | * [ ] `slack_bolt.App` and/or its core components 12 | * [ ] `slack_bolt.async_app.AsyncApp` and/or its core components 13 | * [ ] Adapters in `slack_bolt.adapter` 14 | * [ ] Document pages under `/docs` 15 | * [ ] Others 16 | 17 | ## Requirements 18 | 19 | Please read the [Contributing guidelines](https://github.com/slackapi/bolt-python/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. 20 | 21 | * [ ] I've read and understood the [Contributing Guidelines](https://github.com/slackapi/bolt-python/blob/main/.github/contributing.md) and have done my best effort to follow them. 22 | * [ ] I've read and agree to the [Code of Conduct](https://slackhq.github.io/code-of-conduct). 23 | * [ ] I've run `./scripts/install_all_and_run_tests.sh` after making the changes. 24 | -------------------------------------------------------------------------------- /.github/workflows/flake8.yml: -------------------------------------------------------------------------------- 1 | name: Run flake8 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: 20 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 flake8 verification 27 | run: | 28 | ./scripts/run_flake8.sh 29 | -------------------------------------------------------------------------------- /.github/workflows/mypy.yml: -------------------------------------------------------------------------------- 1 | name: Run 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: 20 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 | docs/_sources/ 5 | docs/.doctrees 6 | .eggs/ 7 | *.egg-info/ 8 | *.egg 9 | *.py[cod] 10 | __pycache__/ 11 | *.so 12 | *~ 13 | 14 | # virtualenv 15 | env*/ 16 | venv/ 17 | .venv* 18 | .env/ 19 | 20 | # codecov / coverage 21 | .coverage 22 | cov_* 23 | coverage.xml 24 | reports/ 25 | 26 | # due to using tox and pytest 27 | .tox 28 | .cache 29 | .pytest_cache/ 30 | .python-version 31 | pip 32 | .mypy_cache/ 33 | .ruby-version 34 | 35 | # JetBrains PyCharm settings 36 | .idea/ 37 | 38 | tmp.txt 39 | .DS_Store 40 | logs/ 41 | 42 | .env* 43 | *.db 44 | 45 | .pytype/ 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020- 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_bolt/py.typed -------------------------------------------------------------------------------- /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/content/concepts/commands.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Listening & responding to commands 3 | lang: en 4 | slug: /concepts/commands 5 | --- 6 | 7 | Your app can use the `command()` method to listen to incoming slash command requests. The method requires a `command_name` of type `str`. 8 | 9 | Commands must be acknowledged with `ack()` to inform Slack your app has received the request. 10 | 11 | There are two ways to respond to slash commands. The first way is to use `say()`, which accepts a string or JSON payload. The second is `respond()` which is a utility for the `response_url`. These are explained in more depth in the [responding to actions](/concepts/actions) section. 12 | 13 | When setting up commands within your app configuration, you'll append `/slack/events` to your request URL. 14 | 15 | Refer to [the module document](https://tools.slack.dev/bolt-python/api-docs/slack_bolt/kwargs_injection/args.html) to learn the available listener arguments. 16 | ```python 17 | # The echo command simply echoes on command 18 | @app.command("/echo") 19 | def repeat_text(ack, respond, command): 20 | # Acknowledge command request 21 | ack() 22 | respond(f"{command['text']}") 23 | ``` 24 | -------------------------------------------------------------------------------- /docs/content/concepts/errors.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Handling errors 3 | lang: en 4 | slug: /concepts/errors 5 | --- 6 | 7 | If an error occurs in a listener, you can handle it directly using a try/except block. Errors associated with your app will be of type `BoltError`. Errors associated with calling Slack APIs will be of type `SlackApiError`. 8 | 9 | By default, the global error handler will log all non-handled exceptions to the console. To handle global errors yourself, you can attach a global error handler to your app using the `app.error(fn)` function. 10 | 11 | ```python 12 | @app.error 13 | def custom_error_handler(error, body, logger): 14 | logger.exception(f"Error: {error}") 15 | logger.info(f"Request body: {body}") 16 | ``` -------------------------------------------------------------------------------- /docs/content/concepts/logging.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Logging 3 | lang: en 4 | slug: /concepts/logging 5 | --- 6 | 7 | By default, Bolt will log information from your app to the output destination. After you've imported the `logging` module, you can customize the root log level by passing the `level` parameter to `basicConfig()`. The available log levels in order of least to most severe are `debug`, `info`, `warning`, `error`, and `critical`. 8 | 9 | Outside of a global context, you can also log a single message corresponding to a specific level. Because Bolt uses Python’s [standard logging module](https://docs.python.org/3/library/logging.html), you can use any its features. 10 | 11 | ```python 12 | import logging 13 | 14 | # logger in a global context 15 | # requires importing logging 16 | logging.basicConfig(level=logging.DEBUG) 17 | 18 | @app.event("app_mention") 19 | def handle_mention(body, say, logger): 20 | user = body["event"]["user"] 21 | # single logger call 22 | # global logger is passed to listener 23 | logger.debug(body) 24 | say(f"{user} mentioned your app") 25 | ``` 26 | -------------------------------------------------------------------------------- /docs/content/concepts/token-rotation.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Token rotation 3 | lang: en 4 | slug: /concepts/token-rotation 5 | --- 6 | 7 | Supported in Bolt for Python as of [v1.7.0](https://github.com/slackapi/bolt-python/releases/tag/v1.7.0), token rotation provides an extra layer of security for your access tokens and is defined by the [OAuth V2 RFC](https://datatracker.ietf.org/doc/html/rfc6749#section-10.4). 8 | 9 | Instead of an access token representing an existing installation of your Slack app indefinitely, with token rotation enabled, access tokens expire. A refresh token acts as a long-lived way to refresh your access tokens. 10 | 11 | Bolt for Python supports and will handle token rotation automatically so long as the [built-in OAuth](/concepts/authenticating-oauth) functionality is used. 12 | 13 | For more information about token rotation, please see the [documentation](https://docs.slack.dev/authentication/using-token-rotation). -------------------------------------------------------------------------------- /docs/content/concepts/web-api.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Using the Web API 3 | lang: en 4 | slug: /concepts/web-api 5 | --- 6 | 7 | You can call [any Web API method](https://docs.slack.dev/reference/methods) using the [`WebClient`](https://tools.slack.dev/python-slack-sdk/web) provided to your Bolt app as either `app.client` or `client` in middleware/listener arguments (given that your app has the appropriate scopes). When you call one the client's methods, it returns a `SlackResponse` which contains the response from Slack. 8 | 9 | The token used to initialize Bolt can be found in the `context` object, which is required to call most Web API methods. 10 | 11 | :::info 12 | 13 | Refer to [the module document](https://tools.slack.dev/bolt-python/api-docs/slack_bolt/kwargs_injection/args.html) to learn the available listener arguments. 14 | 15 | ::: 16 | 17 | ```python 18 | @app.message("wake me up") 19 | def say_hello(client, message): 20 | # Unix Epoch time for September 30, 2020 11:59:59 PM 21 | when_september_ends = 1601510399 22 | channel_id = message["channel"] 23 | client.chat_scheduleMessage( 24 | channel=channel_id, 25 | post_at=when_september_ends, 26 | text="Summer has come and passed" 27 | ) 28 | ``` 29 | -------------------------------------------------------------------------------- /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/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/commands.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: コマンドのリスニングと応答 3 | lang: ja-jp 4 | slug: /concepts/commands 5 | --- 6 | 7 | スラッシュコマンドが実行されたリクエストをリッスンするには、`command()` メソッドを使用します。このメソッドでは `str` 型の `command_name` の指定が必要です。 8 | 9 | コマンドリクエストをアプリが受信し確認したことを Slack に通知するため、`ack()` を呼び出す必要があります。 10 | 11 | スラッシュコマンドに応答する方法は 2 つあります。1 つ目は `say()` を使う方法で、文字列または JSON のペイロードを渡すことができます。2 つ目は `respond()` を使う方法です。これは `response_url` がある場合に活躍します。これらの方法は[アクションへの応答](/concepts/action-respond)セクションで詳しく説明しています。 12 | 13 | アプリの設定でコマンドを登録するときは、リクエスト URL の末尾に `/slack/events` をつけます。 14 | 15 | 指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 16 | ```python 17 | # echoコマンドは受け取ったコマンドをそのまま返す 18 | @app.command("/echo") 19 | def repeat_text(ack, respond, command): 20 | # command リクエストを確認 21 | ack() 22 | respond(f"{command['text']}") 23 | ``` -------------------------------------------------------------------------------- /docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/errors.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: エラーの処理 3 | lang: ja-jp 4 | slug: /concepts/errors 5 | --- 6 | 7 | リスナー内でエラーが発生した場合に try/except ブロックを使用して直接エラーを処理することができます。アプリに関連するエラーは、`BoltError` 型です。Slack API の呼び出しに関連するエラーは、`SlackApiError` 型となります。 8 | 9 | デフォルトでは、すべての処理されなかった例外のログはグローバルのエラーハンドラーによってコンソールに出力されます。グローバルのエラーを開発者自身で処理するには、`app.error(fn)` 関数を使ってグローバルのエラーハンドラーをアプリに設定します。 10 | 11 | ```python 12 | @app.error 13 | def custom_error_handler(error, body, logger): 14 | logger.exception(f"Error: {error}") 15 | logger.info(f"Request body: {body}") 16 | ``` -------------------------------------------------------------------------------- /docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/logging.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: ロギング 3 | lang: ja-jp 4 | slug: /concepts/logging 5 | --- 6 | 7 | デフォルトでは、アプリからのログ情報は、既定の出力先に出力されます。`logging` モジュールをインポートすれば、`basicConfig()` の `level` パラメーターでrootのログレベルを変更することができます。指定できるログレベルは、重要度の低い方から `debug`、`info`、`warning`、`error`、および `critical` です。 8 | 9 | グローバルのコンテキストとは別に、指定のログレベルに応じて単一のメッセージをログ出力することもできます。Bolt では [Python 標準の logging モジュール](https://docs.python.org/3/library/logging.html)が使われているため、このモジュールが持つすべての機能を利用できます。 10 | 11 | ```python 12 | import logging 13 | 14 | # グローバルのコンテキストの logger です 15 | # logging をインポートする必要があります 16 | logging.basicConfig(level=logging.DEBUG) 17 | 18 | @app.event("app_mention") 19 | def handle_mention(body, say, logger): 20 | user = body["event"]["user"] 21 | # 単一の logger の呼び出しです 22 | # グローバルの logger がリスナーに渡されています 23 | logger.debug(body) 24 | say(f"{user} mentioned your app") 25 | ``` -------------------------------------------------------------------------------- /docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/message-listening.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: メッセージのリスニング 3 | lang: ja-jp 4 | slug: /concepts/message-listening 5 | --- 6 | 7 | [あなたのアプリがアクセス権限を持つ](https://docs.slack.dev/messaging/retrieving-messages)メッセージの投稿イベントをリッスンするには `message()` メソッドを利用します。このメソッドは `type` が `message` ではないイベントを処理対象から除外します。 8 | 9 | `message()` の引数には `str` 型または `re.Pattern` オブジェクトを指定できます。この条件のパターンに一致しないメッセージは除外されます。 10 | 11 | 指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 12 | ```python 13 | # '👋' が含まれるすべてのメッセージに一致 14 | @app.message(":wave:") 15 | def say_hello(message, say): 16 | user = message['user'] 17 | say(f"Hi there, <@{user}>!") 18 | ``` 19 | 20 | ## 正規表現パターンの利用 21 | 22 | 文字列の代わりに `re.compile()` メソッドを使用すれば、より細やかな条件指定ができます。 23 | 24 | ```python 25 | import re 26 | 27 | @app.message(re.compile("(hi|hello|hey)")) 28 | def say_hello_regex(say, context): 29 | # 正規表現のマッチ結果は context.matches に設定される 30 | greeting = context['matches'][0] 31 | say(f"{greeting}, how are you?") 32 | ``` -------------------------------------------------------------------------------- /docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/token-rotation.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: トークンのローテーション 3 | lang: ja-jp 4 | slug: /concepts/token-rotation 5 | --- 6 | 7 | Bolt for Python [v1.7.0](https://github.com/slackapi/bolt-python/releases/tag/v1.7.0) から、アクセストークンのさらなるセキュリティ強化のレイヤーであるトークンローテーションの機能に対応しています。トークンローテーションは [OAuth V2 の RFC](https://datatracker.ietf.org/doc/html/rfc6749#section-10.4) で規定されているものです。 8 | 9 | 既存の Slack アプリではアクセストークンが無期限に存在し続けるのに対して、トークンローテーションを有効にしたアプリではアクセストークンが失効するようになります。リフレッシュトークンを利用して、アクセストークンを長期間にわたって更新し続けることができます。 10 | 11 | [Bolt for Python の組み込みの OAuth 機能](/concepts/authenticating-oauth) を使用していれば、Bolt for Python が自動的にトークンローテーションの処理をハンドリングします。 12 | 13 | トークンローテーションに関する詳細は [API ドキュメント](https://docs.slack.dev/authentication/using-token-rotation)を参照してください。 -------------------------------------------------------------------------------- /docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/web-api.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Web API の使い方 3 | lang: ja-jp 4 | slug: /concepts/web-api 5 | --- 6 | 7 | `app.client`、またはミドルウェア・リスナーの引数 `client` として Bolt アプリに提供されている [`WebClient`](https://tools.slack.dev/python-slack-sdk/basic_usage.html) は必要な権限を付与されており、これを利用することで[あらゆる Web API メソッド](https://docs.slack.dev/reference/methods)を呼び出すことができます。このクライアントのメソッドを呼び出すと `SlackResponse` という Slack からの応答情報を含むオブジェクトが返されます。 8 | 9 | Bolt の初期化に使用するトークンは `context` オブジェクトに設定されます。このトークンは、多くの Web API メソッドを呼び出す際に必要となります。 10 | 11 | 指定可能な引数の一覧はモジュールドキュメントを参考にしてください。 12 | ```python 13 | @app.message("wake me up") 14 | def say_hello(client, message): 15 | # 2020 年 9 月 30 日午後 11:59:59 を示す Unix エポック秒 16 | when_september_ends = 1601510399 17 | channel_id = message["channel"] 18 | client.chat_scheduleMessage( 19 | channel=channel_id, 20 | post_at=when_september_ends, 21 | text="Summer has come and passed" 22 | ) 23 | ``` -------------------------------------------------------------------------------- /docs/i18n/ja-jp/docusaurus-theme-classic/footer.json: -------------------------------------------------------------------------------- 1 | { 2 | "copyright": { 3 | "message": "

Made with ♡ by Slack and friends

", 4 | "description": "The footer copyright" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /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/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/.nojekyll -------------------------------------------------------------------------------- /docs/static/img/bolt-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /docs/static/img/bolt-py-logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/static/img/boltpy/basic-information-page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/boltpy/basic-information-page.png -------------------------------------------------------------------------------- /docs/static/img/boltpy/bolt-favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/boltpy/bolt-favicon.png -------------------------------------------------------------------------------- /docs/static/img/boltpy/bot-token.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/boltpy/bot-token.png -------------------------------------------------------------------------------- /docs/static/img/boltpy/ngrok.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/boltpy/ngrok.gif -------------------------------------------------------------------------------- /docs/static/img/boltpy/request-url-config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/boltpy/request-url-config.png -------------------------------------------------------------------------------- /docs/static/img/boltpy/signing-secret.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/boltpy/signing-secret.png -------------------------------------------------------------------------------- /docs/static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/favicon.ico -------------------------------------------------------------------------------- /docs/static/img/slack-logo-on-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/slack-logo-on-white.png -------------------------------------------------------------------------------- /docs/static/img/slack-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /docs/static/img/tutorials/ai-chatbot/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/ai-chatbot/1.png -------------------------------------------------------------------------------- /docs/static/img/tutorials/ai-chatbot/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/ai-chatbot/2.png -------------------------------------------------------------------------------- /docs/static/img/tutorials/ai-chatbot/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/ai-chatbot/3.png -------------------------------------------------------------------------------- /docs/static/img/tutorials/ai-chatbot/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/ai-chatbot/4.png -------------------------------------------------------------------------------- /docs/static/img/tutorials/ai-chatbot/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/ai-chatbot/5.png -------------------------------------------------------------------------------- /docs/static/img/tutorials/ai-chatbot/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/ai-chatbot/6.png -------------------------------------------------------------------------------- /docs/static/img/tutorials/ai-chatbot/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/ai-chatbot/7.png -------------------------------------------------------------------------------- /docs/static/img/tutorials/ai-chatbot/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/ai-chatbot/8.png -------------------------------------------------------------------------------- /docs/static/img/tutorials/custom-steps-jira/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-jira/1.png -------------------------------------------------------------------------------- /docs/static/img/tutorials/custom-steps-jira/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-jira/2.png -------------------------------------------------------------------------------- /docs/static/img/tutorials/custom-steps-jira/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-jira/3.png -------------------------------------------------------------------------------- /docs/static/img/tutorials/custom-steps-jira/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-jira/4.png -------------------------------------------------------------------------------- /docs/static/img/tutorials/custom-steps-jira/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-jira/5.png -------------------------------------------------------------------------------- /docs/static/img/tutorials/custom-steps-jira/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-jira/6.png -------------------------------------------------------------------------------- /docs/static/img/tutorials/custom-steps-jira/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-jira/7.png -------------------------------------------------------------------------------- /docs/static/img/tutorials/custom-steps-wfb-existing/add-step.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-wfb-existing/add-step.png -------------------------------------------------------------------------------- /docs/static/img/tutorials/custom-steps-wfb-existing/app-message.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-wfb-existing/app-message.png -------------------------------------------------------------------------------- /docs/static/img/tutorials/custom-steps-wfb-existing/define-step.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-wfb-existing/define-step.png -------------------------------------------------------------------------------- /docs/static/img/tutorials/custom-steps-wfb-existing/find-step.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-wfb-existing/find-step.png -------------------------------------------------------------------------------- /docs/static/img/tutorials/custom-steps-wfb-existing/inputs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-wfb-existing/inputs.png -------------------------------------------------------------------------------- /docs/static/img/tutorials/custom-steps-wfb-existing/org-ready.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-wfb-existing/org-ready.png -------------------------------------------------------------------------------- /docs/static/img/tutorials/custom-steps-wfb-existing/outputs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-wfb-existing/outputs.png -------------------------------------------------------------------------------- /docs/static/img/tutorials/custom-steps-wfb-existing/step-inputs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-wfb-existing/step-inputs.png -------------------------------------------------------------------------------- /docs/static/img/tutorials/custom-steps-wfb-new/app-token.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-wfb-new/app-token.png -------------------------------------------------------------------------------- /docs/static/img/tutorials/custom-steps-wfb-new/bot-token.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-wfb-new/bot-token.png -------------------------------------------------------------------------------- /docs/static/img/tutorials/custom-steps-wfb-new/install.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-wfb-new/install.png -------------------------------------------------------------------------------- /docs/static/img/tutorials/custom-steps-wfb-new/manifest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-wfb-new/manifest.png -------------------------------------------------------------------------------- /docs/static/img/tutorials/custom-steps-wfb-new/wfb-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-wfb-new/wfb-1.png -------------------------------------------------------------------------------- /docs/static/img/tutorials/custom-steps-wfb-new/wfb-10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-wfb-new/wfb-10.png -------------------------------------------------------------------------------- /docs/static/img/tutorials/custom-steps-wfb-new/wfb-11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-wfb-new/wfb-11.png -------------------------------------------------------------------------------- /docs/static/img/tutorials/custom-steps-wfb-new/wfb-12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-wfb-new/wfb-12.png -------------------------------------------------------------------------------- /docs/static/img/tutorials/custom-steps-wfb-new/wfb-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-wfb-new/wfb-2.png -------------------------------------------------------------------------------- /docs/static/img/tutorials/custom-steps-wfb-new/wfb-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-wfb-new/wfb-3.png -------------------------------------------------------------------------------- /docs/static/img/tutorials/custom-steps-wfb-new/wfb-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-wfb-new/wfb-4.png -------------------------------------------------------------------------------- /docs/static/img/tutorials/custom-steps-wfb-new/wfb-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-wfb-new/wfb-5.png -------------------------------------------------------------------------------- /docs/static/img/tutorials/custom-steps-wfb-new/wfb-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-wfb-new/wfb-6.png -------------------------------------------------------------------------------- /docs/static/img/tutorials/custom-steps-wfb-new/wfb-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-wfb-new/wfb-7.png -------------------------------------------------------------------------------- /docs/static/img/tutorials/custom-steps-wfb-new/wfb-8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-wfb-new/wfb-8.png -------------------------------------------------------------------------------- /docs/static/img/tutorials/custom-steps-wfb-new/wfb-9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-wfb-new/wfb-9.png -------------------------------------------------------------------------------- /docs/static/img/tutorials/custom-steps-wfb-new/workflow-step.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-wfb-new/workflow-step.png -------------------------------------------------------------------------------- /docs/static/img/tutorials/modals/base_link.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/modals/base_link.gif -------------------------------------------------------------------------------- /docs/static/img/tutorials/modals/final_product.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/modals/final_product.gif -------------------------------------------------------------------------------- /docs/static/img/tutorials/modals/heart_icon.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/modals/heart_icon.gif -------------------------------------------------------------------------------- /docs/static/img/tutorials/modals/interactivity_url.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/modals/interactivity_url.png -------------------------------------------------------------------------------- /docs/static/img/tutorials/modals/slash_command.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/modals/slash_command.png -------------------------------------------------------------------------------- /examples/aiohttp_devtools/async_app.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | logging.basicConfig(level=logging.DEBUG) 4 | 5 | from slack_bolt.async_app import AsyncApp 6 | 7 | app = AsyncApp() 8 | 9 | 10 | @app.event("app_mention") 11 | async def event_test(body, say, logger): 12 | logger.info(body) 13 | await say("What's up?") 14 | 15 | 16 | @app.command("/hello-bolt-python") 17 | # or app.command(re.compile(r"/hello-.+"))(test_command) 18 | async def command(ack, body): 19 | user_id = body["user_id"] 20 | await ack(f"Hi <@{user_id}>!") 21 | 22 | 23 | def app_factory(): 24 | return app.web_app() 25 | 26 | 27 | # pip install -r requirements.txt 28 | # export SLACK_SIGNING_SECRET=*** 29 | # export SLACK_BOT_TOKEN=xoxb-*** 30 | # adev runserver --port 3000 --app-factory app_factory async_app.py 31 | -------------------------------------------------------------------------------- /examples/aiohttp_devtools/requirements.txt: -------------------------------------------------------------------------------- 1 | aiohttp>=3,<4 2 | aiohttp-devtools>=0.13,<0.14 -------------------------------------------------------------------------------- /examples/app.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | logging.basicConfig(level=logging.DEBUG) 4 | 5 | from slack_bolt import App 6 | 7 | app = App() 8 | 9 | 10 | @app.middleware # or app.use(log_request) 11 | def log_request(logger, body, next): 12 | logger.debug(body) 13 | return next() 14 | 15 | 16 | @app.command("/hello-bolt-python") 17 | def hello_command(ack, body): 18 | user_id = body["user_id"] 19 | ack(f"Hi <@{user_id}>!") 20 | 21 | 22 | @app.event("app_mention") 23 | def event_test(body, say, logger): 24 | logger.info(body) 25 | say("What's up?") 26 | 27 | 28 | @app.error 29 | def global_error_handler(error, body, logger): 30 | logger.exception(error) 31 | logger.info(body) 32 | 33 | 34 | if __name__ == "__main__": 35 | app.start(3000) 36 | 37 | # pip install slack_bolt 38 | # export SLACK_SIGNING_SECRET=*** 39 | # export SLACK_BOT_TOKEN=xoxb-*** 40 | # python app.py 41 | -------------------------------------------------------------------------------- /examples/app_authorize.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | logging.basicConfig(level=logging.DEBUG) 4 | 5 | import os 6 | from slack_bolt import App 7 | from slack_bolt.authorization import AuthorizeResult 8 | from slack_sdk import WebClient 9 | 10 | 11 | def authorize(enterprise_id, team_id, user_id, client: WebClient, logger): 12 | logger.info(f"{enterprise_id},{team_id},{user_id}") 13 | # You can implement your own logic here 14 | token = os.environ["MY_TOKEN"] 15 | return AuthorizeResult.from_auth_test_response( 16 | auth_test_response=client.auth_test(token=token), 17 | bot_token=token, 18 | ) 19 | 20 | 21 | app = App(signing_secret=os.environ["SLACK_SIGNING_SECRET"], authorize=authorize) 22 | 23 | 24 | @app.command("/hello-bolt-python") 25 | def hello_command(ack, body): 26 | user_id = body["user_id"] 27 | ack(f"Hi <@{user_id}>!") 28 | 29 | 30 | @app.event("app_mention") 31 | def event_test(body, say, logger): 32 | logger.info(body) 33 | say("What's up?") 34 | 35 | 36 | if __name__ == "__main__": 37 | app.start(3000) 38 | 39 | # pip install slack_bolt 40 | # export SLACK_SIGNING_SECRET=*** 41 | # export MY_TOKEN=xoxb-*** 42 | # python app_authorize.py 43 | -------------------------------------------------------------------------------- /examples/asgi/app.py: -------------------------------------------------------------------------------- 1 | from slack_bolt import App 2 | from slack_bolt.adapter.asgi import SlackRequestHandler 3 | 4 | app = App() 5 | 6 | 7 | @app.event("app_mention") 8 | def handle_app_mentions(body, say, logger): 9 | logger.info(body) 10 | say("What's up?") 11 | 12 | 13 | api = SlackRequestHandler(app) 14 | 15 | # pip install -r requirements.txt 16 | # export SLACK_SIGNING_SECRET=*** 17 | # export SLACK_BOT_TOKEN=xoxb-*** 18 | # uvicorn app:api --reload --port 3000 --log-level debug 19 | # ngrok http 3000 20 | -------------------------------------------------------------------------------- /examples/asgi/async_app.py: -------------------------------------------------------------------------------- 1 | from slack_bolt.async_app import AsyncApp 2 | from slack_bolt.adapter.asgi.async_handler import AsyncSlackRequestHandler 3 | 4 | app = AsyncApp() 5 | 6 | 7 | @app.event("app_mention") 8 | async def handle_app_mentions(body, say, logger): 9 | logger.info(body) 10 | await say("What's up?") 11 | 12 | 13 | api = AsyncSlackRequestHandler(app) 14 | 15 | # pip install -r requirements.txt 16 | # export SLACK_SIGNING_SECRET=*** 17 | # export SLACK_BOT_TOKEN=xoxb-*** 18 | # uvicorn async_app:api --reload --port 3000 --log-level debug 19 | # ngrok http 3000 20 | -------------------------------------------------------------------------------- /examples/asgi/async_oauth_app.py: -------------------------------------------------------------------------------- 1 | from slack_bolt.async_app import AsyncApp 2 | from slack_bolt.adapter.asgi.async_handler import AsyncSlackRequestHandler 3 | 4 | app = AsyncApp() 5 | 6 | 7 | @app.event("app_mention") 8 | async def handle_app_mentions(body, say, logger): 9 | logger.info(body) 10 | await say("What's up?") 11 | 12 | 13 | api = AsyncSlackRequestHandler(app) 14 | 15 | # pip install -r requirements.txt 16 | 17 | # # -- OAuth flow -- # 18 | # export SLACK_SIGNING_SECRET=*** 19 | # export SLACK_CLIENT_ID=111.111 20 | # export SLACK_CLIENT_SECRET=*** 21 | # export SLACK_SCOPES=app_mentions:read,channels:history,im:history,chat:write 22 | 23 | # uvicorn async_oauth_app:api --reload --port 3000 --log-level debug 24 | -------------------------------------------------------------------------------- /examples/asgi/oauth_app.py: -------------------------------------------------------------------------------- 1 | from slack_bolt import App 2 | from slack_bolt.adapter.asgi import SlackRequestHandler 3 | 4 | app = App() 5 | 6 | 7 | @app.event("app_mention") 8 | def handle_app_mentions(body, say, logger): 9 | logger.info(body) 10 | say("What's up?") 11 | 12 | 13 | api = SlackRequestHandler(app) 14 | 15 | # pip install -r requirements.txt 16 | 17 | # # -- OAuth flow -- # 18 | # export SLACK_SIGNING_SECRET=*** 19 | # export SLACK_CLIENT_ID=111.111 20 | # export SLACK_CLIENT_SECRET=*** 21 | # export SLACK_SCOPES=app_mentions:read,channels:history,im:history,chat:write 22 | 23 | # uvicorn oauth_app:api --reload --port 3000 --log-level debug 24 | -------------------------------------------------------------------------------- /examples/asgi/requirements.txt: -------------------------------------------------------------------------------- 1 | uvicorn<1 2 | aiohttp>=3,<4 3 | -------------------------------------------------------------------------------- /examples/async_app.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | logging.basicConfig(level=logging.DEBUG) 4 | 5 | from slack_bolt.async_app import AsyncApp 6 | 7 | app = AsyncApp() 8 | 9 | 10 | @app.middleware # or app.use(log_request) 11 | async def log_request(logger, body, next): 12 | logger.debug(body) 13 | return await next() 14 | 15 | 16 | @app.event("app_mention") 17 | async def event_test(body, say, logger): 18 | logger.info(body) 19 | await say("What's up?") 20 | 21 | 22 | @app.command("/hello-bolt-python") 23 | # or app.command(re.compile(r"/hello-.+"))(test_command) 24 | async def command(ack, body): 25 | user_id = body["user_id"] 26 | await ack(f"Hi <@{user_id}>!") 27 | 28 | 29 | if __name__ == "__main__": 30 | app.start(3000) 31 | 32 | # pip install slack_bolt 33 | # export SLACK_SIGNING_SECRET=*** 34 | # export SLACK_BOT_TOKEN=xoxb-*** 35 | # python async_app.py 36 | -------------------------------------------------------------------------------- /examples/aws_chalice/.chalice/config.json.oauth: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0", 3 | "app_name": "bolt-python-chalice", 4 | "stages": { 5 | "dev": { 6 | "api_gateway_stage": "api", 7 | "environment_variables": { 8 | "SLACK_SIGNING_SECRET": "xxx", 9 | "SLACK_CLIENT_ID": "111.111", 10 | "SLACK_CLIENT_SECRET": "xxx", 11 | "SLACK_SCOPES": "app_mentions:read,channels:history,im:history,chat:write", 12 | "SLACK_INSTALLATION_S3_BUCKET_NAME": "", 13 | "SLACK_STATE_S3_BUCKET_NAME": "" 14 | }, 15 | "manage_iam_role": false, 16 | "iam_role_arn": "arn:aws:iam::1111111111111:role/bolt_python_s3_storage" 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /examples/aws_chalice/.chalice/config.json.simple: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0", 3 | "app_name": "bolt-python-chalice", 4 | "stages": { 5 | "dev": { 6 | "api_gateway_stage": "api", 7 | "environment_variables": { 8 | "SLACK_BOT_TOKEN": "xoxb-xxx", 9 | "SLACK_SIGNING_SECRET": "xxx" 10 | }, 11 | "manage_iam_role": false, 12 | "iam_role_arn": "arn:aws:iam::1111111111111:role/bolt_python_lambda_invocation" 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /examples/aws_chalice/.gitignore: -------------------------------------------------------------------------------- 1 | .chalice/config.json 2 | .chalice/deployments/ 3 | .chalice/venv/ 4 | .chalice/deployed/ 5 | vendor/ -------------------------------------------------------------------------------- /examples/aws_chalice/deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # configure aws credentials properly 4 | pip install -U chalice click boto3 5 | pip install -r requirements.txt 6 | # edit .chalice/config.json 7 | rm -rf vendor && mkdir -p vendor/slack_bolt && cp -pr ../../slack_bolt/* vendor/slack_bolt/ 8 | chalice deploy 9 | -------------------------------------------------------------------------------- /examples/aws_chalice/requirements.txt: -------------------------------------------------------------------------------- 1 | slack_sdk -------------------------------------------------------------------------------- /examples/aws_lambda/.env.oauth_sample: -------------------------------------------------------------------------------- 1 | export SLACK_SIGNING_SECRET= 2 | export SLACK_CLIENT_ID= 3 | export SLACK_CLIENT_SECRET= 4 | export SLACK_SCOPES=app_mentions:read,channels:history,im:history,chat:write 5 | export SLACK_INSTALLATION_S3_BUCKET_NAME= 6 | export SLACK_STATE_S3_BUCKET_NAME= 7 | -------------------------------------------------------------------------------- /examples/aws_lambda/.env.sample: -------------------------------------------------------------------------------- 1 | export SLACK_BOT_TOKEN= 2 | export SLACK_SIGNING_SECRET= -------------------------------------------------------------------------------- /examples/aws_lambda/.gitignore: -------------------------------------------------------------------------------- 1 | slack-bolt/ 2 | slack_bolt/ 3 | vendor/ 4 | .env 5 | -------------------------------------------------------------------------------- /examples/aws_lambda/aws_lambda.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from slack_bolt import App 4 | from slack_bolt.adapter.aws_lambda import SlackRequestHandler 5 | 6 | # process_before_response must be True when running on FaaS 7 | app = App(process_before_response=True) 8 | 9 | 10 | @app.event("app_mention") 11 | def handle_app_mentions(body, say, logger): 12 | logger.info(body) 13 | say("What's up?") 14 | 15 | 16 | @app.command("/hello-bolt-python-lambda") 17 | def respond_to_slack_within_3_seconds(ack): 18 | ack("Thanks!") 19 | 20 | 21 | SlackRequestHandler.clear_all_log_handlers() 22 | logging.basicConfig(format="%(asctime)s %(message)s", level=logging.DEBUG) 23 | 24 | 25 | def handler(event, context): 26 | slack_handler = SlackRequestHandler(app=app) 27 | return slack_handler.handle(event, context) 28 | 29 | 30 | # export SLACK_SIGNING_SECRET=*** 31 | # export SLACK_BOT_TOKEN=xoxb-*** 32 | 33 | # rm -rf vendor && cp -pr ../../src/* vendor/ 34 | # pip install git+https://github.com/nficano/python-lambda 35 | # lambda deploy --config-file aws_lambda_config.yaml --requirements requirements.txt 36 | -------------------------------------------------------------------------------- /examples/aws_lambda/deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | rm -rf vendor && mkdir -p vendor/slack_bolt && cp -pr ../../slack_bolt/* vendor/slack_bolt/ 3 | pip install git+https://github.com/nficano/python-lambda 4 | lambda deploy \ 5 | --config-file aws_lambda_config.yaml \ 6 | --requirements requirements.txt 7 | -------------------------------------------------------------------------------- /examples/aws_lambda/deploy_lazy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | rm -rf slack_bolt && mkdir slack_bolt && cp -pr ../../slack_bolt/* slack_bolt/ 3 | pip install git+https://github.com/nficano/python-lambda 4 | lambda deploy \ 5 | --config-file lazy_aws_lambda_config.yaml \ 6 | --requirements requirements.txt 7 | -------------------------------------------------------------------------------- /examples/aws_lambda/deploy_oauth.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | rm -rf slack_bolt && mkdir slack_bolt && cp -pr ../../slack_bolt/* slack_bolt/ 3 | pip install git+https://github.com/nficano/python-lambda 4 | lambda deploy \ 5 | --config-file aws_lambda_oauth_config.yaml \ 6 | --requirements requirements_oauth.txt 7 | -------------------------------------------------------------------------------- /examples/aws_lambda/requirements.txt: -------------------------------------------------------------------------------- 1 | slack_sdk -------------------------------------------------------------------------------- /examples/aws_lambda/requirements_oauth.txt: -------------------------------------------------------------------------------- 1 | slack_sdk -------------------------------------------------------------------------------- /examples/bottle/app.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | logging.basicConfig(level=logging.DEBUG) 4 | 5 | from slack_bolt import App 6 | from slack_bolt.adapter.bottle import SlackRequestHandler 7 | 8 | app = App() 9 | 10 | 11 | @app.middleware # or app.use(log_request) 12 | def log_request(logger, body, next): 13 | logger.debug(body) 14 | return next() 15 | 16 | 17 | @app.event("app_mention") 18 | def event_test(ack, body, say, logger): 19 | logger.info(body) 20 | say("What's up?") 21 | 22 | 23 | from bottle import post, request, response, run 24 | 25 | handler = SlackRequestHandler(app) 26 | 27 | 28 | @post("/slack/events") 29 | def slack_events(): 30 | return handler.handle(request, response) 31 | 32 | 33 | if __name__ == "__main__": 34 | run(host="0.0.0.0", port=3000, reloader=True) 35 | 36 | # pip install -r requirements.txt 37 | # export SLACK_SIGNING_SECRET=*** 38 | # export SLACK_BOT_TOKEN=xoxb-*** 39 | # python app.py 40 | -------------------------------------------------------------------------------- /examples/bottle/requirements.txt: -------------------------------------------------------------------------------- 1 | bottle>=0.12,<1 -------------------------------------------------------------------------------- /examples/cherrypy/app.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | logging.basicConfig(level=logging.DEBUG) 4 | 5 | from slack_bolt import App 6 | from slack_bolt.adapter.cherrypy import SlackRequestHandler 7 | 8 | app = App() 9 | 10 | 11 | @app.middleware # or app.use(log_request) 12 | def log_request(logger, body, next): 13 | logger.debug(body) 14 | return next() 15 | 16 | 17 | @app.command("/hello-bolt-python") 18 | def hello_command(ack): 19 | ack("Hi from CherryPy") 20 | 21 | 22 | @app.event("app_mention") 23 | def event_test(body, say, logger): 24 | logger.info(body) 25 | say("What's up?") 26 | 27 | 28 | import cherrypy 29 | 30 | handler = SlackRequestHandler(app) 31 | 32 | 33 | class SlackApp(object): 34 | @cherrypy.expose 35 | @cherrypy.tools.slack_in() 36 | def events(self, **kwargs): 37 | return handler.handle() 38 | 39 | 40 | if __name__ == "__main__": 41 | cherrypy.config.update({"server.socket_port": 3000}) 42 | cherrypy.quickstart(SlackApp(), "/slack") 43 | 44 | # pip install -r requirements.txt 45 | # export SLACK_SIGNING_SECRET=*** 46 | # export SLACK_BOT_TOKEN=xoxb-*** 47 | # python app.py 48 | -------------------------------------------------------------------------------- /examples/cherrypy/requirements.txt: -------------------------------------------------------------------------------- 1 | CherryPy>=18,<19 2 | -------------------------------------------------------------------------------- /examples/django/.gitignore: -------------------------------------------------------------------------------- 1 | db.sqlite3 2 | db/ 3 | -------------------------------------------------------------------------------- /examples/django/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os 4 | import sys 5 | 6 | 7 | def main(): 8 | """Run administrative tasks.""" 9 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myslackapp.settings") 10 | try: 11 | from django.core.management import execute_from_command_line 12 | except ImportError as exc: 13 | raise ImportError( 14 | "Couldn't import Django. Are you sure it's installed and " 15 | "available on your PYTHONPATH environment variable? Did you " 16 | "forget to activate a virtual environment?" 17 | ) from exc 18 | execute_from_command_line(sys.argv) 19 | 20 | 21 | if __name__ == "__main__": 22 | main() 23 | -------------------------------------------------------------------------------- /examples/django/myslackapp/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/examples/django/myslackapp/__init__.py -------------------------------------------------------------------------------- /examples/django/myslackapp/asgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | ASGI config for myslackapp project. 3 | 4 | It exposes the ASGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.2/howto/deployment/asgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.asgi import get_asgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myslackapp.settings") 15 | 16 | application = get_asgi_application() 17 | -------------------------------------------------------------------------------- /examples/django/myslackapp/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for myslackapp project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.2/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myslackapp.settings") 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /examples/django/mysql-docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.9' 2 | services: 3 | db: 4 | image: mysql:8 5 | environment: 6 | MYSQL_DATABASE: slackapp 7 | MYSQL_USER: app 8 | MYSQL_PASSWORD: password 9 | MYSQL_ROOT_PASSWORD: password 10 | #command: 11 | # - '--wait_timeout=3' 12 | volumes: 13 | - './db:/var/lib/mysql' 14 | ports: 15 | - 33306:3306 16 | 17 | -------------------------------------------------------------------------------- /examples/django/oauth_app/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/examples/django/oauth_app/__init__.py -------------------------------------------------------------------------------- /examples/django/oauth_app/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class OauthAppConfig(AppConfig): 5 | default_auto_field = "django.db.models.BigAutoField" 6 | name = "oauth_app" 7 | -------------------------------------------------------------------------------- /examples/django/oauth_app/migrations/0002_token_rotation.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.3 on 2021-07-15 23:44 2 | 3 | from django.db import migrations 4 | from django.db import models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ("oauth_app", "0001_initial"), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField("SlackBot", "bot_refresh_token", models.TextField(null=True)), 15 | migrations.AddField("SlackBot", "bot_token_expires_at", models.DateTimeField(null=True)), 16 | migrations.AddField("SlackInstallation", "bot_refresh_token", models.TextField(null=True)), 17 | migrations.AddField("SlackInstallation", "bot_token_expires_at", models.DateTimeField(null=True)), 18 | migrations.AddField("SlackInstallation", "user_refresh_token", models.TextField(null=True)), 19 | migrations.AddField( 20 | "SlackInstallation", 21 | "user_token_expires_at", 22 | models.DateTimeField(null=True), 23 | ), 24 | ] 25 | -------------------------------------------------------------------------------- /examples/django/oauth_app/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/examples/django/oauth_app/migrations/__init__.py -------------------------------------------------------------------------------- /examples/django/oauth_app/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from django.http import HttpRequest 4 | from django.views.decorators.csrf import csrf_exempt 5 | 6 | from slack_bolt.adapter.django import SlackRequestHandler 7 | from .slack_listeners import app 8 | 9 | handler = SlackRequestHandler(app=app) 10 | 11 | 12 | @csrf_exempt 13 | def slack_events_handler(request: HttpRequest): 14 | return handler.handle(request) 15 | 16 | 17 | def slack_oauth_handler(request: HttpRequest): 18 | return handler.handle(request) 19 | 20 | 21 | urlpatterns = [ 22 | path("slack/events", slack_events_handler, name="handle"), 23 | path("slack/install", slack_oauth_handler, name="install"), 24 | path("slack/oauth_redirect", slack_oauth_handler, name="oauth_redirect"), 25 | ] 26 | -------------------------------------------------------------------------------- /examples/django/requirements.txt: -------------------------------------------------------------------------------- 1 | Django>=3.2,<4 2 | slack-bolt>=1.7,<2 3 | -------------------------------------------------------------------------------- /examples/django/simple_app/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/examples/django/simple_app/__init__.py -------------------------------------------------------------------------------- /examples/django/simple_app/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class SimpleAppConfig(AppConfig): 5 | default_auto_field = "django.db.models.BigAutoField" 6 | name = "simple_app" 7 | -------------------------------------------------------------------------------- /examples/django/simple_app/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/examples/django/simple_app/migrations/__init__.py -------------------------------------------------------------------------------- /examples/django/simple_app/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models # noqa: F401 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /examples/django/simple_app/slack_listeners.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | 4 | from slack_bolt import App 5 | 6 | logger = logging.getLogger(__name__) 7 | 8 | app = App( 9 | token=os.environ["SLACK_BOT_TOKEN"], 10 | signing_secret=os.environ["SLACK_SIGNING_SECRET"], 11 | # disable eagerly verifying the given SLACK_BOT_TOKEN value 12 | token_verification_enabled=False, 13 | ) 14 | 15 | 16 | @app.event("app_mention") 17 | def handle_app_mentions(logger, event, say): 18 | logger.info(event) 19 | say(f"Hi there, <@{event['user']}>") 20 | -------------------------------------------------------------------------------- /examples/django/simple_app/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from slack_bolt.adapter.django import SlackRequestHandler 4 | from .slack_listeners import app 5 | 6 | handler = SlackRequestHandler(app=app) 7 | 8 | from django.http import HttpRequest 9 | from django.views.decorators.csrf import csrf_exempt 10 | 11 | 12 | @csrf_exempt 13 | def slack_events_handler(request: HttpRequest): 14 | return handler.handle(request) 15 | 16 | 17 | urlpatterns = [ 18 | path("slack/events", slack_events_handler, name="slack_events"), 19 | ] 20 | -------------------------------------------------------------------------------- /examples/docker/aiohttp/Dockerfile: -------------------------------------------------------------------------------- 1 | # 2 | # docker build . -t your-repo/hello-bolt 3 | # 4 | FROM python:3.8.5-slim-buster as builder 5 | RUN apt-get update && apt-get clean 6 | COPY requirements.txt /build/ 7 | WORKDIR /build/ 8 | RUN pip install -U pip && pip install -r requirements.txt 9 | 10 | FROM python:3.8.5-slim-buster as app 11 | COPY --from=builder /build/ /app/ 12 | COPY --from=builder /usr/local/lib/ /usr/local/lib/ 13 | WORKDIR /app/ 14 | COPY *.py /app/ 15 | ENTRYPOINT python main.py 16 | 17 | # 18 | # docker run -e SLACK_SIGNING_SECRET=$SLACK_SIGNING_SECRET -e SLACK_BOT_TOKEN=$SLACK_BOT_TOKEN -e PORT=3000 -p 3000:3000 -it your-repo/hello-bolt 19 | # -------------------------------------------------------------------------------- /examples/docker/aiohttp/main.py: -------------------------------------------------------------------------------- 1 | import os 2 | import logging 3 | 4 | from slack_bolt.async_app import AsyncApp 5 | 6 | logging.basicConfig(level=logging.DEBUG) 7 | app = AsyncApp() 8 | 9 | 10 | @app.command("/hello-bolt-python") 11 | async def hello(body, ack): 12 | user_id = body["user_id"] 13 | await ack(f"Hi <@{user_id}>!") 14 | 15 | 16 | if __name__ == "__main__": 17 | app.start(port=int(os.environ.get("PORT", int(os.environ.get("PORT", 3000))))) 18 | -------------------------------------------------------------------------------- /examples/docker/aiohttp/requirements.txt: -------------------------------------------------------------------------------- 1 | slack_bolt 2 | aiohttp>=3,<4 -------------------------------------------------------------------------------- /examples/docker/asgi/Dockerfile: -------------------------------------------------------------------------------- 1 | # 2 | # docker build . -t your-repo/hello-bolt 3 | # 4 | FROM python:3.11-alpine3.17 as builder 5 | COPY requirements.txt /build/ 6 | WORKDIR /build/ 7 | RUN pip install -U pip && pip install -r requirements.txt 8 | 9 | FROM python:3.11-alpine3.17 as app 10 | WORKDIR /app/ 11 | COPY --from=builder /usr/local/bin/ /usr/local/bin/ 12 | COPY --from=builder /usr/local/lib/ /usr/local/lib/ 13 | COPY main.py /app/ 14 | ENTRYPOINT uvicorn main:asgi_app --port $PORT --workers 1 15 | 16 | # 17 | # docker run -e SLACK_SIGNING_SECRET=$SLACK_SIGNING_SECRET -e SLACK_BOT_TOKEN=$SLACK_BOT_TOKEN -e PORT=3000 -p 3000:3000 -it your-repo/hello-bolt 18 | # 19 | -------------------------------------------------------------------------------- /examples/docker/asgi/main.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from slack_bolt import App 3 | from slack_bolt.adapter.asgi import SlackRequestHandler 4 | 5 | logging.basicConfig(level=logging.DEBUG) 6 | app = App() 7 | 8 | 9 | @app.event("app_mention") 10 | def handle_app_mentions(body, say, logger): 11 | logger.info(body) 12 | say("What's up?") 13 | 14 | 15 | asgi_app = SlackRequestHandler(app) 16 | -------------------------------------------------------------------------------- /examples/docker/asgi/requirements.txt: -------------------------------------------------------------------------------- 1 | slack_bolt 2 | uvicorn<1 3 | -------------------------------------------------------------------------------- /examples/docker/fastapi-gunicorn/Dockerfile: -------------------------------------------------------------------------------- 1 | # 2 | # docker build . -t your-repo/hello-bolt 3 | # 4 | FROM tiangolo/uvicorn-gunicorn:python3.8-slim 5 | WORKDIR /app/ 6 | COPY *.py /app/ 7 | COPY requirements.txt /app/ 8 | RUN pip install -U pip && pip install -r requirements.txt 9 | 10 | # 11 | # docker run -e SLACK_SIGNING_SECRET=$SLACK_SIGNING_SECRET -e SLACK_BOT_TOKEN=$SLACK_BOT_TOKEN -e VARIABLE_NAME="api" -p 80:80 -it your-repo/hello-bolt 12 | # or 13 | # docker run -e SLACK_SIGNING_SECRET=$SLACK_SIGNING_SECRET -e SLACK_BOT_TOKEN=$SLACK_BOT_TOKEN -e VARIABLE_NAME="api" -p 3000:80 -it your-repo/hello-bolt 14 | -------------------------------------------------------------------------------- /examples/docker/fastapi-gunicorn/main.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from slack_bolt.async_app import AsyncApp 4 | 5 | logging.basicConfig(level=logging.DEBUG) 6 | app = AsyncApp() 7 | 8 | 9 | @app.command("/hello-bolt-python") 10 | async def hello(body, ack): 11 | user_id = body["user_id"] 12 | await ack(f"Hi <@{user_id}>!") 13 | 14 | 15 | from fastapi import FastAPI, Request 16 | 17 | api = FastAPI() 18 | 19 | from slack_bolt.adapter.fastapi.async_handler import AsyncSlackRequestHandler 20 | 21 | app_handler = AsyncSlackRequestHandler(app) 22 | 23 | 24 | @api.post("/slack/events") 25 | async def endpoint(req: Request): 26 | return await app_handler.handle(req) 27 | -------------------------------------------------------------------------------- /examples/docker/fastapi-gunicorn/requirements.txt: -------------------------------------------------------------------------------- 1 | slack_bolt 2 | fastapi<1 3 | aiohttp>=3,<4 -------------------------------------------------------------------------------- /examples/docker/flask-gunicorn/Dockerfile: -------------------------------------------------------------------------------- 1 | # 2 | # docker build . -t your-repo/hello-bolt 3 | # 4 | FROM python:3.8.5-slim-buster as builder 5 | COPY requirements.txt /build/ 6 | WORKDIR /build/ 7 | RUN pip install -U pip && pip install -r requirements.txt 8 | 9 | FROM python:3.8.5-slim-buster as app 10 | WORKDIR /app/ 11 | COPY --from=builder /usr/local/bin/ /usr/local/bin/ 12 | COPY --from=builder /usr/local/lib/ /usr/local/lib/ 13 | COPY *.py /app/ 14 | ENTRYPOINT gunicorn --bind :$PORT --workers 1 --threads 2 --timeout 0 main:flask_app 15 | 16 | # 17 | # docker run -e SLACK_SIGNING_SECRET=$SLACK_SIGNING_SECRET -e SLACK_BOT_TOKEN=$SLACK_BOT_TOKEN -e PORT=3000 -p 3000:3000 -it your-repo/hello-bolt 18 | # -------------------------------------------------------------------------------- /examples/docker/flask-gunicorn/main.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from slack_bolt import App 4 | 5 | logging.basicConfig(level=logging.DEBUG) 6 | app = App() 7 | 8 | 9 | @app.command("/hello-bolt-python") 10 | def hello(body, ack): 11 | user_id = body["user_id"] 12 | ack(f"Hi <@{user_id}>!") 13 | 14 | 15 | from flask import Flask, request 16 | from slack_bolt.adapter.flask import SlackRequestHandler 17 | 18 | flask_app = Flask(__name__) 19 | handler = SlackRequestHandler(app) 20 | 21 | 22 | @flask_app.route("/slack/events", methods=["POST"]) 23 | def slack_events(): 24 | return handler.handle(request) 25 | -------------------------------------------------------------------------------- /examples/docker/flask-gunicorn/requirements.txt: -------------------------------------------------------------------------------- 1 | slack_bolt 2 | Flask>=1.1 3 | gunicorn>=20 -------------------------------------------------------------------------------- /examples/docker/flask-uwsgi/Dockerfile: -------------------------------------------------------------------------------- 1 | # 2 | # docker build . -t your-repo/hello-bolt 3 | # 4 | FROM python:3.8.5-slim-buster as builder 5 | RUN apt-get update \ 6 | && apt-get -y install build-essential libpcre3-dev \ 7 | && apt-get clean 8 | COPY requirements.txt /build/ 9 | WORKDIR /build/ 10 | RUN pip install -U pip && pip install -r requirements.txt 11 | 12 | FROM python:3.8.5-slim-buster as app 13 | WORKDIR /app/ 14 | COPY --from=builder /usr/local/bin/ /usr/local/bin/ 15 | COPY --from=builder /usr/local/lib/ /usr/local/lib/ 16 | COPY *.py /app/ 17 | COPY uwsgi.ini /app/ 18 | ENTRYPOINT uwsgi --ini uwsgi.ini --http :$PORT 19 | 20 | # 21 | # docker run -e SLACK_SIGNING_SECRET=$SLACK_SIGNING_SECRET -e SLACK_BOT_TOKEN=$SLACK_BOT_TOKEN -e PORT=3000 -p 3000:3000 -it your-repo/hello-bolt 22 | # -------------------------------------------------------------------------------- /examples/docker/flask-uwsgi/main.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from slack_bolt import App 4 | 5 | logging.basicConfig(level=logging.DEBUG) 6 | app = App() 7 | 8 | 9 | @app.command("/hello-bolt-python") 10 | def hello(body, ack): 11 | user_id = body["user_id"] 12 | ack(f"Hi <@{user_id}>!") 13 | 14 | 15 | from flask import Flask, request 16 | from slack_bolt.adapter.flask import SlackRequestHandler 17 | 18 | flask_app = Flask(__name__) 19 | handler = SlackRequestHandler(app) 20 | 21 | 22 | @flask_app.route("/slack/events", methods=["POST"]) 23 | def slack_events(): 24 | return handler.handle(request) 25 | -------------------------------------------------------------------------------- /examples/docker/flask-uwsgi/requirements.txt: -------------------------------------------------------------------------------- 1 | slack_bolt 2 | Flask>1 3 | uWSGI>=2,<3 -------------------------------------------------------------------------------- /examples/docker/flask-uwsgi/uwsgi.ini: -------------------------------------------------------------------------------- 1 | [uwsgi] 2 | module = main:flask_app 3 | master = true 4 | uid = nobody 5 | socket = /tmp/uwsgi.sock 6 | die-on-term = true 7 | enable-threads = true -------------------------------------------------------------------------------- /examples/docker/sanic/Dockerfile: -------------------------------------------------------------------------------- 1 | # 2 | # docker build . -t your-repo/hello-bolt 3 | # 4 | FROM python:3.8.5-slim-buster as builder 5 | COPY requirements.txt /build/ 6 | WORKDIR /build/ 7 | RUN pip install -U pip && pip install -r requirements.txt 8 | 9 | FROM python:3.8.5-slim-buster as app 10 | WORKDIR /app/ 11 | COPY *.py /app/ 12 | COPY --from=builder /usr/local/bin/ /usr/local/bin/ 13 | COPY --from=builder /usr/local/lib/ /usr/local/lib/ 14 | ENTRYPOINT python main.py 15 | 16 | # 17 | # docker run -e SLACK_SIGNING_SECRET=$SLACK_SIGNING_SECRET -e SLACK_BOT_TOKEN=$SLACK_BOT_TOKEN -e PORT=3000 -p 3000:3000 -it your-repo/hello-bolt 18 | # 19 | -------------------------------------------------------------------------------- /examples/docker/sanic/main.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | 4 | from slack_bolt.async_app import AsyncApp 5 | from slack_bolt.adapter.sanic import AsyncSlackRequestHandler 6 | 7 | logging.basicConfig(level=logging.DEBUG) 8 | app = AsyncApp() 9 | app_handler = AsyncSlackRequestHandler(app) 10 | 11 | 12 | @app.command("/hello-bolt-python") 13 | async def hello(body, ack): 14 | user_id = body["user_id"] 15 | await ack(f"Hi <@{user_id}>!") 16 | 17 | 18 | from sanic import Sanic 19 | from sanic.request import Request 20 | 21 | api = Sanic(name="awesome-slack-app") 22 | 23 | 24 | @api.post("/slack/events") 25 | async def endpoint(req: Request): 26 | return await app_handler.handle(req) 27 | 28 | 29 | if __name__ == "__main__": 30 | api.run(host="0.0.0.0", port=int(os.environ.get("PORT", 3000))) 31 | -------------------------------------------------------------------------------- /examples/docker/sanic/requirements.txt: -------------------------------------------------------------------------------- 1 | slack_bolt 2 | aiohttp>=3,<4 3 | sanic>=20,<21 -------------------------------------------------------------------------------- /examples/events_app.py: -------------------------------------------------------------------------------- 1 | import re 2 | import logging 3 | 4 | logging.basicConfig(level=logging.DEBUG) 5 | 6 | from slack_bolt import App 7 | 8 | app = App() 9 | 10 | 11 | @app.middleware # or app.use(log_request) 12 | def log_request(logger, body, next): 13 | logger.debug(body) 14 | return next() 15 | 16 | 17 | @app.event("app_mention") 18 | def event_test(body, say, logger): 19 | logger.info(body) 20 | say("What's up?") 21 | 22 | 23 | @app.event("reaction_added") 24 | def say_something_to_reaction(say): 25 | say("OK!") 26 | 27 | 28 | @app.message("test") 29 | def test_message(logger, body): 30 | logger.info(body) 31 | 32 | 33 | @app.message(re.compile("bug")) 34 | def mention_bug(logger, body): 35 | logger.info(body) 36 | 37 | 38 | @app.event("message") 39 | def ack_the_rest_of_message_events(logger, body): 40 | logger.info(body) 41 | 42 | 43 | if __name__ == "__main__": 44 | app.start(3000) 45 | 46 | # pip install slack_bolt 47 | # export SLACK_SIGNING_SECRET=*** 48 | # export SLACK_BOT_TOKEN=xoxb-*** 49 | # python events_app.py 50 | -------------------------------------------------------------------------------- /examples/falcon/requirements.txt: -------------------------------------------------------------------------------- 1 | falcon>=2,<3 2 | gunicorn>=20,<21 3 | uvicorn 4 | -------------------------------------------------------------------------------- /examples/fastapi/app.py: -------------------------------------------------------------------------------- 1 | from slack_bolt import App 2 | from slack_bolt.adapter.fastapi import SlackRequestHandler 3 | 4 | app = App() 5 | app_handler = SlackRequestHandler(app) 6 | 7 | 8 | @app.event("app_mention") 9 | def handle_app_mentions(body, say, logger): 10 | logger.info(body) 11 | say("What's up?") 12 | 13 | 14 | @app.event("message") 15 | def handle_message(): 16 | pass 17 | 18 | 19 | from fastapi import FastAPI, Request 20 | 21 | api = FastAPI() 22 | 23 | 24 | @api.post("/slack/events") 25 | async def endpoint(req: Request): 26 | return await app_handler.handle(req) 27 | 28 | 29 | # pip install -r requirements.txt 30 | # export SLACK_SIGNING_SECRET=*** 31 | # export SLACK_BOT_TOKEN=xoxb-*** 32 | # uvicorn app:api --reload --port 3000 --log-level warning 33 | -------------------------------------------------------------------------------- /examples/fastapi/async_app.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | logging.basicConfig(level=logging.DEBUG) 4 | 5 | from slack_bolt.async_app import AsyncApp 6 | from slack_bolt.adapter.fastapi.async_handler import AsyncSlackRequestHandler 7 | 8 | app = AsyncApp() 9 | app_handler = AsyncSlackRequestHandler(app) 10 | 11 | 12 | @app.event("app_mention") 13 | async def handle_app_mentions(body, say, logger): 14 | logger.info(body) 15 | await say("What's up?") 16 | 17 | 18 | @app.event("message") 19 | async def handle_message(): 20 | pass 21 | 22 | 23 | from fastapi import FastAPI, Request 24 | 25 | api = FastAPI() 26 | 27 | 28 | @api.post("/slack/events") 29 | async def endpoint(req: Request): 30 | return await app_handler.handle(req) 31 | 32 | 33 | # pip install -r requirements.txt 34 | # export SLACK_SIGNING_SECRET=*** 35 | # export SLACK_BOT_TOKEN=xoxb-*** 36 | # uvicorn async_app:api --reload --port 3000 --log-level warning 37 | -------------------------------------------------------------------------------- /examples/fastapi/async_app_custom_props.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | logging.basicConfig(level=logging.DEBUG) 4 | 5 | from slack_bolt.async_app import AsyncApp 6 | from slack_bolt.adapter.fastapi.async_handler import AsyncSlackRequestHandler 7 | 8 | app = AsyncApp() 9 | app_handler = AsyncSlackRequestHandler(app) 10 | 11 | 12 | @app.event("app_mention") 13 | async def handle_app_mentions(context, say, logger): 14 | logger.info(context) 15 | assert context.get("foo") == "FOO" 16 | await say("What's up?") 17 | 18 | 19 | @app.event("message") 20 | async def handle_message(): 21 | pass 22 | 23 | 24 | from fastapi import FastAPI, Request, Depends 25 | 26 | api = FastAPI() 27 | 28 | 29 | def get_foo(): 30 | yield "FOO" 31 | 32 | 33 | @api.post("/slack/events") 34 | async def endpoint(req: Request, foo: str = Depends(get_foo)): 35 | return await app_handler.handle(req, {"foo": foo}) 36 | 37 | 38 | # pip install -r requirements.txt 39 | # export SLACK_SIGNING_SECRET=*** 40 | # export SLACK_BOT_TOKEN=xoxb-*** 41 | # uvicorn async_app_custom_props:api --reload --port 3000 --log-level warning 42 | -------------------------------------------------------------------------------- /examples/fastapi/oauth_app.py: -------------------------------------------------------------------------------- 1 | from slack_bolt import App 2 | from slack_bolt.adapter.fastapi import SlackRequestHandler 3 | 4 | app = App() 5 | app_handler = SlackRequestHandler(app) 6 | 7 | 8 | @app.event("app_mention") 9 | def handle_app_mentions(body, say, logger): 10 | logger.info(body) 11 | say("What's up?") 12 | 13 | 14 | @app.event("message") 15 | def handle_message(): 16 | pass 17 | 18 | 19 | from fastapi import FastAPI, Request 20 | 21 | api = FastAPI() 22 | 23 | 24 | @api.post("/slack/events") 25 | async def endpoint(req: Request): 26 | return await app_handler.handle(req) 27 | 28 | 29 | @api.get("/slack/install") 30 | async def install(req: Request): 31 | return await app_handler.handle(req) 32 | 33 | 34 | @api.get("/slack/oauth_redirect") 35 | async def oauth_redirect(req: Request): 36 | return await app_handler.handle(req) 37 | 38 | 39 | # pip install -r requirements.txt 40 | 41 | # # -- OAuth flow -- # 42 | # export SLACK_SIGNING_SECRET=*** 43 | # export SLACK_CLIENT_ID=111.111 44 | # export SLACK_CLIENT_SECRET=*** 45 | # export SLACK_SCOPES=app_mentions:read,channels:history,im:history,chat:write 46 | 47 | # uvicorn oauth_app:api --reload --port 3000 --log-level warning 48 | -------------------------------------------------------------------------------- /examples/fastapi/requirements.txt: -------------------------------------------------------------------------------- 1 | fastapi<1 2 | uvicorn<1 3 | -------------------------------------------------------------------------------- /examples/flask/app.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | logging.basicConfig(level=logging.DEBUG) 4 | 5 | from slack_bolt import App 6 | from slack_bolt.adapter.flask import SlackRequestHandler 7 | 8 | app = App() 9 | 10 | 11 | @app.middleware # or app.use(log_request) 12 | def log_request(logger, body, next): 13 | logger.debug(body) 14 | return next() 15 | 16 | 17 | @app.event("app_mention") 18 | def event_test(body, say, logger): 19 | logger.info(body) 20 | say("What's up?") 21 | 22 | 23 | @app.event("message") 24 | def handle_message(): 25 | pass 26 | 27 | 28 | from flask import Flask, request 29 | 30 | flask_app = Flask(__name__) 31 | handler = SlackRequestHandler(app) 32 | 33 | 34 | @flask_app.route("/slack/events", methods=["POST"]) 35 | def slack_events(): 36 | return handler.handle(request) 37 | 38 | 39 | # pip install -r requirements.txt 40 | # export SLACK_SIGNING_SECRET=*** 41 | # export SLACK_BOT_TOKEN=xoxb-*** 42 | # FLASK_APP=app.py FLASK_ENV=development flask run -p 3000 43 | -------------------------------------------------------------------------------- /examples/flask/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask>1 2 | -------------------------------------------------------------------------------- /examples/getting_started/.gitignore: -------------------------------------------------------------------------------- 1 | # Python 2 | .venv/ 3 | env*/ -------------------------------------------------------------------------------- /examples/getting_started/requirements.txt: -------------------------------------------------------------------------------- 1 | slack-bolt -------------------------------------------------------------------------------- /examples/google_app_engine/flask/.gcloudignore: -------------------------------------------------------------------------------- 1 | # This file specifies files that are *not* uploaded to Google Cloud Platform 2 | # using gcloud. It follows the same syntax as .gitignore, with the addition of 3 | # "#!include" directives (which insert the entries of the given .gitignore-style 4 | # file at that point). 5 | # 6 | # For more information, run: 7 | # $ gcloud topic gcloudignore 8 | # 9 | .gcloudignore 10 | # If you would like to upload your .git directory, .gitignore file or files 11 | # from your .gitignore file, remove the corresponding line 12 | # below: 13 | .git 14 | .gitignore 15 | 16 | # Python pycache: 17 | __pycache__/ 18 | # Ignored by the build system 19 | /setup.cfg -------------------------------------------------------------------------------- /examples/google_app_engine/flask/.gitignore: -------------------------------------------------------------------------------- 1 | env_variables.yaml -------------------------------------------------------------------------------- /examples/google_app_engine/flask/app.yaml: -------------------------------------------------------------------------------- 1 | runtime: python38 2 | 3 | inbound_services: 4 | - warmup 5 | 6 | automatic_scaling: 7 | min_idle_instances: 1 8 | 9 | 10 | handlers: 11 | - url: /slack/events 12 | secure: always 13 | script: auto 14 | 15 | includes: 16 | - env_variables.yaml 17 | -------------------------------------------------------------------------------- /examples/google_app_engine/flask/env_variables.yaml.sample: -------------------------------------------------------------------------------- 1 | env_variables: 2 | SLACK_BOT_TOKEN: "xoxb-xxx" 3 | SLACK_SIGNING_SECRET: "yyy" -------------------------------------------------------------------------------- /examples/google_app_engine/flask/main.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | 4 | from slack_bolt import App 5 | 6 | logging.basicConfig(level=logging.DEBUG) 7 | bolt_app = App() 8 | 9 | 10 | @bolt_app.command("/hey-google-app-engine") 11 | def hello(body, ack): 12 | user_id = body["user_id"] 13 | ack(f"Hi <@{user_id}>!") 14 | 15 | 16 | from flask import Flask, request 17 | from slack_bolt.adapter.flask import SlackRequestHandler 18 | 19 | app = Flask(__name__) 20 | handler = SlackRequestHandler(bolt_app) 21 | 22 | 23 | @app.route("/_ah/warmup") 24 | def warmup(): 25 | # Handle your warmup logic here, e.g. set up a database connection pool 26 | return "", 200, {} 27 | 28 | 29 | @app.route("/slack/events", methods=["POST"]) 30 | def slack_events(): 31 | return handler.handle(request) 32 | 33 | 34 | # Only for local debug 35 | if __name__ == "__main__": 36 | app.run(debug=True, host="0.0.0.0", port=int(os.environ.get("PORT", 3000))) 37 | -------------------------------------------------------------------------------- /examples/google_app_engine/flask/requirements.txt: -------------------------------------------------------------------------------- 1 | slack_bolt 2 | Flask>=1.1,<2 -------------------------------------------------------------------------------- /examples/google_cloud_functions/.env.yaml.oauth-sample: -------------------------------------------------------------------------------- 1 | SLACK_CLIENT_ID: '1111.222' 2 | SLACK_CLIENT_SECRET: 'xxx' 3 | SLACK_SIGNING_SECRET: 'yyy' 4 | SLACK_SCOPES: 'app_mentions:read,chat:write,commands' 5 | -------------------------------------------------------------------------------- /examples/google_cloud_functions/.env.yaml.sample: -------------------------------------------------------------------------------- 1 | SLACK_SIGNING_SECRET: xxx 2 | SLACK_BOT_TOKEN: xoxb-xxx -------------------------------------------------------------------------------- /examples/google_cloud_functions/.gcloudignore: -------------------------------------------------------------------------------- 1 | # This file specifies files that are *not* uploaded to Google Cloud Platform 2 | # using gcloud. It follows the same syntax as .gitignore, with the addition of 3 | # "#!include" directives (which insert the entries of the given .gitignore-style 4 | # file at that point). 5 | # 6 | # For more information, run: 7 | # $ gcloud topic gcloudignore 8 | # 9 | .gcloudignore 10 | # If you would like to upload your .git directory, .gitignore file or files 11 | # from your .gitignore file, remove the corresponding line 12 | # below: 13 | .git 14 | .gitignore 15 | .env.yaml.sample 16 | 17 | node_modules 18 | #!include:.gitignore 19 | -------------------------------------------------------------------------------- /examples/google_cloud_functions/.gitignore: -------------------------------------------------------------------------------- 1 | .env.yaml 2 | main.py 3 | -------------------------------------------------------------------------------- /examples/google_cloud_functions/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask>1 2 | slack_bolt 3 | google-cloud-datastore>=2.1.0,<3 -------------------------------------------------------------------------------- /examples/google_cloud_run/aiohttp/Dockerfile: -------------------------------------------------------------------------------- 1 | # 2 | # https://cloud.google.com/run/docs/quickstarts/build-and-deploy 3 | # 4 | # export PROJECT_ID=`gcloud config get-value project` 5 | # export SLACK_SIGNING_SECRET= 6 | # export SLACK_BOT_TOKEN= 7 | # gcloud builds submit --tag gcr.io/$PROJECT_ID/helloworld 8 | # gcloud run deploy helloworld --image gcr.io/$PROJECT_ID/helloworld --platform managed --update-env-vars SLACK_SIGNING_SECRET=$SLACK_SIGNING_SECRET,SLACK_BOT_TOKEN=$SLACK_BOT_TOKEN 9 | # 10 | 11 | # ---------------------------------------------- 12 | # Use the official lightweight Python image. 13 | # https://hub.docker.com/_/python 14 | FROM python:3.8.5-slim-buster 15 | 16 | # Allow statements and log messages to immediately appear in the Knative logs 17 | ENV PYTHONUNBUFFERED True 18 | 19 | # Copy local code to the container image. 20 | ENV APP_HOME /app 21 | WORKDIR $APP_HOME 22 | COPY . ./ 23 | 24 | # Install production dependencies. 25 | RUN pip install -U pip && pip install -r requirements.txt 26 | 27 | # Start AIOHTTP server 28 | ENTRYPOINT python main.py 29 | -------------------------------------------------------------------------------- /examples/google_cloud_run/aiohttp/main.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | 4 | from slack_bolt.async_app import AsyncApp 5 | 6 | logging.basicConfig(level=logging.DEBUG) 7 | app = AsyncApp() 8 | 9 | 10 | @app.command("/hey-google") 11 | async def hello(body, ack): 12 | user_id = body["user_id"] 13 | await ack(f"Hi <@{user_id}>!") 14 | 15 | 16 | if __name__ == "__main__": 17 | app.start(port=int(os.environ.get("PORT", 3000))) 18 | -------------------------------------------------------------------------------- /examples/google_cloud_run/aiohttp/requirements.txt: -------------------------------------------------------------------------------- 1 | slack_bolt 2 | aiohttp>=3,<4 -------------------------------------------------------------------------------- /examples/google_cloud_run/flask-gunicorn/Dockerfile: -------------------------------------------------------------------------------- 1 | # 2 | # https://cloud.google.com/run/docs/quickstarts/build-and-deploy 3 | # 4 | # export PROJECT_ID=`gcloud config get-value project` 5 | # export SLACK_SIGNING_SECRET= 6 | # export SLACK_BOT_TOKEN= 7 | # gcloud builds submit --tag gcr.io/$PROJECT_ID/helloworld 8 | # gcloud run deploy helloworld --image gcr.io/$PROJECT_ID/helloworld --platform managed --update-env-vars SLACK_SIGNING_SECRET=$SLACK_SIGNING_SECRET,SLACK_BOT_TOKEN=$SLACK_BOT_TOKEN 9 | # 10 | 11 | # ---------------------------------------------- 12 | # Use the official lightweight Python image. 13 | # https://hub.docker.com/_/python 14 | FROM python:3.8.5-slim-buster 15 | 16 | # Allow statements and log messages to immediately appear in the Knative logs 17 | ENV PYTHONUNBUFFERED True 18 | 19 | # Copy local code to the container image. 20 | ENV APP_HOME /app 21 | WORKDIR $APP_HOME 22 | COPY . ./ 23 | 24 | # Install production dependencies. 25 | RUN pip install -U pip && pip install -r requirements.txt 26 | 27 | # Run the web service on container startup. 28 | ENTRYPOINT gunicorn --bind :$PORT --workers 1 --threads 2 --timeout 0 main:flask_app 29 | -------------------------------------------------------------------------------- /examples/google_cloud_run/flask-gunicorn/main.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | 4 | from slack_bolt import App 5 | 6 | logging.basicConfig(level=logging.DEBUG) 7 | app = App() 8 | 9 | 10 | @app.command("/hey-google") 11 | def hello(body, ack): 12 | user_id = body["user_id"] 13 | ack(f"Hi <@{user_id}>!") 14 | 15 | 16 | from flask import Flask, request 17 | from slack_bolt.adapter.flask import SlackRequestHandler 18 | 19 | flask_app = Flask(__name__) 20 | handler = SlackRequestHandler(app) 21 | 22 | 23 | @flask_app.route("/slack/events", methods=["POST"]) 24 | def slack_events(): 25 | return handler.handle(request) 26 | 27 | 28 | # Only for local debug 29 | if __name__ == "__main__": 30 | flask_app.run(debug=True, host="0.0.0.0", port=int(os.environ.get("PORT", 3000))) 31 | -------------------------------------------------------------------------------- /examples/google_cloud_run/flask-gunicorn/requirements.txt: -------------------------------------------------------------------------------- 1 | slack_bolt 2 | Flask>=1.1 3 | gunicorn>=20 -------------------------------------------------------------------------------- /examples/google_cloud_run/sanic/Dockerfile: -------------------------------------------------------------------------------- 1 | # 2 | # https://cloud.google.com/run/docs/quickstarts/build-and-deploy 3 | # 4 | # export PROJECT_ID=`gcloud config get-value project` 5 | # export SLACK_SIGNING_SECRET= 6 | # export SLACK_BOT_TOKEN= 7 | # gcloud builds submit --tag gcr.io/$PROJECT_ID/helloworld 8 | # gcloud run deploy helloworld --image gcr.io/$PROJECT_ID/helloworld --platform managed --update-env-vars SLACK_SIGNING_SECRET=$SLACK_SIGNING_SECRET,SLACK_BOT_TOKEN=$SLACK_BOT_TOKEN 9 | # 10 | 11 | # ---------------------------------------------- 12 | # Use the official lightweight Python image. 13 | # https://hub.docker.com/_/python 14 | FROM python:3.8.5-slim-buster 15 | 16 | # Allow statements and log messages to immediately appear in the Knative logs 17 | ENV PYTHONUNBUFFERED True 18 | 19 | # Copy local code to the container image. 20 | ENV APP_HOME /app 21 | WORKDIR $APP_HOME 22 | COPY . ./ 23 | 24 | # Install production dependencies. 25 | RUN pip install -U pip && pip install -r requirements.txt 26 | 27 | # Start Sanic server 28 | ENTRYPOINT python main.py 29 | -------------------------------------------------------------------------------- /examples/google_cloud_run/sanic/main.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | 4 | from slack_bolt.async_app import AsyncApp 5 | from slack_bolt.adapter.sanic import AsyncSlackRequestHandler 6 | 7 | logging.basicConfig(level=logging.DEBUG) 8 | app = AsyncApp() 9 | app_handler = AsyncSlackRequestHandler(app) 10 | 11 | 12 | @app.command("/hey-google") 13 | async def hello(body, ack): 14 | user_id = body["user_id"] 15 | await ack(f"Hi <@{user_id}>!") 16 | 17 | 18 | from sanic import Sanic 19 | from sanic.request import Request 20 | 21 | api = Sanic(name="awesome-slack-app") 22 | 23 | 24 | @api.post("/slack/events") 25 | async def endpoint(req: Request): 26 | return await app_handler.handle(req) 27 | 28 | 29 | if __name__ == "__main__": 30 | api.run(host="0.0.0.0", port=int(os.environ.get("PORT", 3000))) 31 | -------------------------------------------------------------------------------- /examples/google_cloud_run/sanic/requirements.txt: -------------------------------------------------------------------------------- 1 | slack_bolt 2 | aiohttp>=3,<4 3 | sanic>=20,<21 -------------------------------------------------------------------------------- /examples/heroku/Procfile: -------------------------------------------------------------------------------- 1 | web: gunicorn --bind :$PORT --workers 1 --threads 10 --timeout 0 main:flask_app 2 | -------------------------------------------------------------------------------- /examples/heroku/main.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from slack_bolt import App 4 | 5 | logging.basicConfig(level=logging.DEBUG) 6 | app = App() 7 | 8 | 9 | @app.command("/hello-bolt-python-heroku") 10 | def hello(body, ack): 11 | user_id = body["user_id"] 12 | ack(f"Hi <@{user_id}>!") 13 | 14 | 15 | from flask import Flask, request 16 | from slack_bolt.adapter.flask import SlackRequestHandler 17 | 18 | flask_app = Flask(__name__) 19 | handler = SlackRequestHandler(app) 20 | 21 | 22 | @flask_app.route("/slack/events", methods=["POST"]) 23 | def slack_events(): 24 | return handler.handle(request) 25 | 26 | 27 | # heroku login 28 | # heroku create 29 | # git remote add heroku https://git.heroku.com/xxx.git 30 | 31 | # export SLACK_BOT_TOKEN=xxx 32 | # export SLACK_SIGNING_SECRET=xxx 33 | # heroku config:set SLACK_BOT_TOKEN=$SLACK_BOT_TOKEN 34 | # heroku config:set SLACK_SIGNING_SECRET=$SLACK_SIGNING_SECRET 35 | # git checkout -b main 36 | # git add . 37 | # git commit -m'Initial commit for my awesome Slack app' 38 | # git push heroku main 39 | -------------------------------------------------------------------------------- /examples/heroku/requirements.txt: -------------------------------------------------------------------------------- 1 | slack_bolt 2 | Flask>=1.1 3 | gunicorn>=20 -------------------------------------------------------------------------------- /examples/oauth_sqlite3_app.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | logging.basicConfig(level=logging.DEBUG) 4 | 5 | from slack_bolt import App 6 | from slack_bolt.oauth import OAuthFlow 7 | 8 | app = App( 9 | oauth_flow=OAuthFlow.sqlite3( 10 | database="./slackapp.db", 11 | token_rotation_expiration_minutes=60 * 24, # for testing 12 | ) 13 | ) 14 | 15 | 16 | @app.event("app_mention") 17 | def handle_app_mentions(body, say, logger): 18 | logger.info(body) 19 | say("What's up?") 20 | 21 | 22 | @app.command("/token-rotation-modal") 23 | def handle_some_command(ack, body, logger): 24 | ack() 25 | logger.info(body) 26 | 27 | 28 | if __name__ == "__main__": 29 | app.start(3000) 30 | 31 | # pip install slack_bolt 32 | # export SLACK_SIGNING_SECRET=*** 33 | # export SLACK_BOT_TOKEN=xoxb-*** 34 | # export SLACK_CLIENT_ID=111.111 35 | # export SLACK_CLIENT_SECRET=*** 36 | # export SLACK_SCOPES=app_mentions:read,channels:history,im:history,chat:write 37 | # python oauth_app.py 38 | -------------------------------------------------------------------------------- /examples/oauth_sqlite3_app_bot_only.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | logging.basicConfig(level=logging.DEBUG) 4 | 5 | from slack_bolt import App 6 | from slack_bolt.oauth import OAuthFlow 7 | 8 | app = App( 9 | oauth_flow=OAuthFlow.sqlite3( 10 | database="./slackapp.db", 11 | token_rotation_expiration_minutes=60 * 24, # for testing 12 | ), 13 | installation_store_bot_only=True, 14 | ) 15 | 16 | 17 | @app.event("app_mention") 18 | def handle_app_mentions(body, say, logger): 19 | logger.info(body) 20 | say("What's up?") 21 | 22 | 23 | @app.command("/token-rotation-modal") 24 | def handle_some_command(ack, body, logger): 25 | ack() 26 | logger.info(body) 27 | 28 | 29 | if __name__ == "__main__": 30 | app.start(3000) 31 | 32 | # pip install slack_bolt 33 | # export SLACK_SIGNING_SECRET=*** 34 | # export SLACK_BOT_TOKEN=xoxb-*** 35 | # export SLACK_CLIENT_ID=111.111 36 | # export SLACK_CLIENT_SECRET=*** 37 | # export SLACK_SCOPES=app_mentions:read,channels:history,im:history,chat:write 38 | # python oauth_app.py 39 | -------------------------------------------------------------------------------- /examples/pyramid/app.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from slack_bolt import App 3 | from slack_bolt.adapter.pyramid.handler import SlackRequestHandler 4 | 5 | logging.basicConfig(level=logging.DEBUG) 6 | app = App() 7 | 8 | 9 | @app.event("app_mention") 10 | def event_test(body, say, logger): 11 | logger.info(body) 12 | say("What's up?") 13 | 14 | 15 | handler = SlackRequestHandler(app) 16 | 17 | if __name__ == "__main__": 18 | from wsgiref.simple_server import make_server 19 | from pyramid.config import Configurator 20 | 21 | with Configurator() as config: 22 | config.add_route("slack_events", "/slack/events") 23 | config.add_view(handler.handle, route_name="slack_events", request_method="POST") 24 | pyramid_app = config.make_wsgi_app() 25 | server = make_server("0.0.0.0", 3000, pyramid_app) 26 | server.serve_forever() 27 | 28 | # export SLACK_SIGNING_SECRET=*** 29 | # export SLACK_BOT_TOKEN=xoxb-*** 30 | # python app.py 31 | -------------------------------------------------------------------------------- /examples/pyramid/requirements.txt: -------------------------------------------------------------------------------- 1 | pyramid>=1,<2 2 | -------------------------------------------------------------------------------- /examples/sanic/async_app.py: -------------------------------------------------------------------------------- 1 | import os 2 | from slack_bolt.async_app import AsyncApp 3 | from slack_bolt.adapter.sanic import AsyncSlackRequestHandler 4 | 5 | app = AsyncApp() 6 | app_handler = AsyncSlackRequestHandler(app) 7 | 8 | 9 | @app.event("app_mention") 10 | async def handle_app_mentions(body, say, logger): 11 | logger.info(body) 12 | await say("What's up?") 13 | 14 | 15 | from sanic import Sanic 16 | from sanic.request import Request 17 | 18 | api = Sanic(name="awesome-slack-app") 19 | 20 | 21 | @api.post("/slack/events") 22 | async def endpoint(req: Request): 23 | return await app_handler.handle(req) 24 | 25 | 26 | if __name__ == "__main__": 27 | api.run(host="0.0.0.0", port=int(os.environ.get("PORT", 3000))) 28 | 29 | 30 | # pip install -r requirements.txt 31 | # export SLACK_SIGNING_SECRET=*** 32 | # export SLACK_BOT_TOKEN=xoxb-*** 33 | # uvicorn async_app:api --reload --port 3000 --log-level debug 34 | -------------------------------------------------------------------------------- /examples/sanic/requirements.txt: -------------------------------------------------------------------------------- 1 | sanic>=20,<21 2 | uvicorn<1 3 | -------------------------------------------------------------------------------- /examples/socket_mode_proxy.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | logging.basicConfig(level=logging.DEBUG) 4 | 5 | import os 6 | 7 | from slack_sdk import WebClient 8 | from slack_bolt import App 9 | from slack_bolt.adapter.socket_mode import SocketModeHandler 10 | 11 | # pip3 install proxy.py 12 | # proxy --port 9000 --log-level d 13 | proxy_url = "http://localhost:9000" 14 | 15 | # Install the Slack app and get xoxb- token in advance 16 | app = App(client=WebClient(token=os.environ["SLACK_BOT_TOKEN"], proxy=proxy_url)) 17 | 18 | 19 | @app.event("app_mention") 20 | def event_test(event, say): 21 | say(f"Hi there, <@{event['user']}>!") 22 | 23 | 24 | if __name__ == "__main__": 25 | # export SLACK_APP_TOKEN=xapp-*** 26 | # export SLACK_BOT_TOKEN=xoxb-*** 27 | SocketModeHandler( 28 | app=app, 29 | app_token=os.environ["SLACK_APP_TOKEN"], 30 | proxy=proxy_url, 31 | ).start() 32 | -------------------------------------------------------------------------------- /examples/sqlalchemy/requirements.txt: -------------------------------------------------------------------------------- 1 | slack_bolt>=0.6 2 | Flask>1 3 | SQLAlchemy 4 | -------------------------------------------------------------------------------- /examples/sqlalchemy/requirements_async.txt: -------------------------------------------------------------------------------- 1 | slack_bolt>=0.6 2 | sanic>=20,<21 3 | uvicorn<1 4 | databases[sqlite] -------------------------------------------------------------------------------- /examples/starlette/app.py: -------------------------------------------------------------------------------- 1 | from slack_bolt import App 2 | from slack_bolt.adapter.starlette import SlackRequestHandler 3 | 4 | app = App() 5 | 6 | 7 | @app.event("app_mention") 8 | def handle_app_mentions(body, say, logger): 9 | logger.info(body) 10 | say("What's up?") 11 | 12 | 13 | app_handler = SlackRequestHandler(app) 14 | 15 | from starlette.applications import Starlette 16 | from starlette.requests import Request 17 | from starlette.routing import Route 18 | 19 | 20 | async def endpoint(req: Request): 21 | return await app_handler.handle(req) 22 | 23 | 24 | api = Starlette(debug=True, routes=[Route("/slack/events", endpoint=endpoint, methods=["POST"])]) 25 | 26 | # pip install -r requirements.txt 27 | # export SLACK_SIGNING_SECRET=*** 28 | # export SLACK_BOT_TOKEN=xoxb-*** 29 | # uvicorn app:api --reload --port 3000 --log-level debug 30 | -------------------------------------------------------------------------------- /examples/starlette/async_app.py: -------------------------------------------------------------------------------- 1 | from slack_bolt.async_app import AsyncApp 2 | from slack_bolt.adapter.starlette.async_handler import AsyncSlackRequestHandler 3 | 4 | app = AsyncApp() 5 | 6 | 7 | @app.event("app_mention") 8 | async def handle_app_mentions(body, say, logger): 9 | logger.info(body) 10 | await say("What's up?") 11 | 12 | 13 | app_handler = AsyncSlackRequestHandler(app) 14 | 15 | from starlette.applications import Starlette 16 | from starlette.requests import Request 17 | from starlette.routing import Route 18 | 19 | 20 | async def endpoint(req: Request): 21 | return await app_handler.handle(req) 22 | 23 | 24 | api = Starlette(debug=True, routes=[Route("/slack/events", endpoint=endpoint, methods=["POST"])]) 25 | 26 | # pip install -r requirements.txt 27 | # export SLACK_SIGNING_SECRET=*** 28 | # export SLACK_BOT_TOKEN=xoxb-*** 29 | # uvicorn async_app:api --reload --port 3000 --log-level debug 30 | -------------------------------------------------------------------------------- /examples/starlette/requirements.txt: -------------------------------------------------------------------------------- 1 | starlette>=0.13,<1 2 | uvicorn<1 3 | -------------------------------------------------------------------------------- /examples/tornado/app.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | logging.basicConfig(level=logging.DEBUG) 4 | 5 | from slack_bolt import App 6 | from slack_bolt.adapter.tornado import SlackEventsHandler 7 | 8 | app = App() 9 | 10 | 11 | @app.middleware # or app.use(log_request) 12 | def log_request(logger, body, next_): 13 | logger.debug(body) 14 | next_() 15 | 16 | 17 | @app.event("app_mention") 18 | def event_test(body, say, logger): 19 | logger.info(body) 20 | say("What's up?") 21 | 22 | 23 | from tornado.web import Application 24 | from tornado.ioloop import IOLoop 25 | 26 | api = Application([("/slack/events", SlackEventsHandler, dict(app=app))]) 27 | 28 | if __name__ == "__main__": 29 | api.listen(3000) 30 | IOLoop.current().start() 31 | 32 | # pip install -r requirements.txt 33 | # export SLACK_SIGNING_SECRET=*** 34 | # export SLACK_BOT_TOKEN=xoxb-*** 35 | # python app.py 36 | -------------------------------------------------------------------------------- /examples/tornado/async_app.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | logging.basicConfig(level=logging.DEBUG) 4 | 5 | from slack_bolt.async_app import AsyncApp 6 | from slack_bolt.adapter.tornado.async_handler import AsyncSlackEventsHandler 7 | 8 | app = AsyncApp() 9 | 10 | 11 | @app.middleware # or app.use(log_request) 12 | async def log_request(logger, body, next_): 13 | logger.debug(body) 14 | await next_() 15 | 16 | 17 | @app.event("app_mention") 18 | async def event_test(body, say, logger): 19 | logger.info(body) 20 | await say("What's up?") 21 | 22 | 23 | from tornado.web import Application 24 | from tornado.ioloop import IOLoop 25 | 26 | api = Application([("/slack/events", AsyncSlackEventsHandler, dict(app=app))]) 27 | 28 | if __name__ == "__main__": 29 | api.listen(3000) 30 | IOLoop.current().start() 31 | 32 | # pip install -r requirements.txt 33 | # export SLACK_SIGNING_SECRET=*** 34 | # export SLACK_BOT_TOKEN=xoxb-*** 35 | # python async_app.py 36 | -------------------------------------------------------------------------------- /examples/tornado/oauth_app.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from slack_bolt import App 3 | from slack_bolt.adapter.tornado import SlackEventsHandler, SlackOAuthHandler 4 | 5 | logging.basicConfig(level=logging.DEBUG) 6 | app = App() 7 | 8 | 9 | @app.middleware # or app.use(log_request) 10 | def log_request(logger, body, next): 11 | logger.debug(body) 12 | next() 13 | 14 | 15 | @app.event("app_mention") 16 | def event_test(body, say, logger): 17 | logger.info(body) 18 | say("What's up?") 19 | 20 | 21 | from tornado.web import Application 22 | from tornado.ioloop import IOLoop 23 | 24 | api = Application( 25 | [ 26 | ("/slack/events", SlackEventsHandler, dict(app=app)), 27 | ("/slack/install", SlackOAuthHandler, dict(app=app)), 28 | ("/slack/oauth_redirect", SlackOAuthHandler, dict(app=app)), 29 | ] 30 | ) 31 | 32 | if __name__ == "__main__": 33 | api.listen(3000) 34 | IOLoop.current().start() 35 | 36 | # pip install -r requirements.txt 37 | 38 | # # -- OAuth flow -- # 39 | # export SLACK_SIGNING_SECRET=*** 40 | # export SLACK_BOT_TOKEN=xoxb-*** 41 | # export SLACK_CLIENT_ID=111.111 42 | # export SLACK_CLIENT_SECRET=*** 43 | # export SLACK_SCOPES=app_mentions:read,chat:write 44 | 45 | # python oauth_app.py 46 | -------------------------------------------------------------------------------- /examples/tornado/requirements.txt: -------------------------------------------------------------------------------- 1 | tornado>=6,<7 -------------------------------------------------------------------------------- /examples/wsgi/app.py: -------------------------------------------------------------------------------- 1 | from slack_bolt import App 2 | from slack_bolt.adapter.wsgi import SlackRequestHandler 3 | 4 | app = App() 5 | 6 | 7 | @app.event("app_mention") 8 | def handle_app_mentions(body, say, logger): 9 | logger.info(body) 10 | say("What's up?") 11 | 12 | 13 | api = SlackRequestHandler(app) 14 | 15 | # pip install -r requirements.txt 16 | # export SLACK_SIGNING_SECRET=*** 17 | # export SLACK_BOT_TOKEN=xoxb-*** 18 | # gunicorn app:api -b 0.0.0.0:3000 --log-level debug 19 | # ngrok http 3000 20 | -------------------------------------------------------------------------------- /examples/wsgi/oauth_app.py: -------------------------------------------------------------------------------- 1 | from slack_bolt import App 2 | from slack_bolt.adapter.wsgi import SlackRequestHandler 3 | 4 | app = App() 5 | 6 | 7 | @app.event("app_mention") 8 | def handle_app_mentions(body, say, logger): 9 | logger.info(body) 10 | say("What's up?") 11 | 12 | 13 | api = SlackRequestHandler(app) 14 | 15 | # pip install -r requirements.txt 16 | 17 | # # -- OAuth flow -- # 18 | # export SLACK_SIGNING_SECRET=*** 19 | # export SLACK_CLIENT_ID=111.111 20 | # export SLACK_CLIENT_SECRET=*** 21 | # export SLACK_SCOPES=app_mentions:read,channels:history,im:history,chat:write 22 | 23 | # gunicorn oauth_app:api -b 0.0.0.0:3000 --log-level debug 24 | -------------------------------------------------------------------------------- /examples/wsgi/requirements.txt: -------------------------------------------------------------------------------- 1 | gunicorn<23 2 | -------------------------------------------------------------------------------- /logs/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/logs/.gitkeep -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | slack_sdk>=3.35.0,<4 2 | -------------------------------------------------------------------------------- /requirements/adapter.txt: -------------------------------------------------------------------------------- 1 | # pip install -r requirements/adapter.txt 2 | # NOTE: any of async ones requires pip install -r requirements/async.txt too 3 | # used only under slack_bolt/adapter 4 | boto3<=2 5 | bottle>=0.12,<1 6 | chalice<=1.27.3; python_version=="3.6" 7 | chalice>=1.28,<2; python_version>"3.6" 8 | CherryPy>=18,<19 9 | Django>=3,<6 10 | falcon>=2,<5; python_version<"3.11" 11 | falcon>=3.1.1,<5; python_version>="3.11" 12 | fastapi>=0.70.0,<1 13 | Flask>=1,<4 14 | Werkzeug>=2,<4 15 | pyramid>=1,<3 16 | sanic>=20,<21; python_version=="3.6" 17 | sanic>=21,<24; python_version>"3.6" and python_version<="3.8" 18 | sanic>=21,<26; python_version>"3.8" 19 | starlette>=0.19.1,<1 20 | tornado>=6,<7 21 | uvicorn<1 # The oldest version can vary among Python runtime versions 22 | gunicorn>=20,<24 23 | websocket_client>=1.2.3,<2 # Socket Mode 3rd party implementation 24 | -------------------------------------------------------------------------------- /requirements/adapter_testing.txt: -------------------------------------------------------------------------------- 1 | # pip install -r requirements/adapter_testing.txt 2 | moto>=3,<6 # For AWS tests 3 | docker>=5,<8 # Used by moto 4 | boddle>=0.2,<0.3 # For Bottle app tests 5 | sanic-testing>=0.7; python_version>"3.6" 6 | -------------------------------------------------------------------------------- /requirements/async.txt: -------------------------------------------------------------------------------- 1 | # pip install -r requirements/async.txt 2 | aiohttp>=3,<4 3 | websockets<16 4 | -------------------------------------------------------------------------------- /requirements/testing.txt: -------------------------------------------------------------------------------- 1 | # pip install -r requirements/testing.txt 2 | -r testing_without_asyncio.txt 3 | -r async.txt 4 | pytest-asyncio<1; 5 | -------------------------------------------------------------------------------- /requirements/testing_without_asyncio.txt: -------------------------------------------------------------------------------- 1 | # pip install -r requirements/testing_without_asyncio.txt 2 | pytest>=6.2.5,<8.4 # https://github.com/tornadoweb/tornado/issues/3375 3 | pytest-cov>=3,<7 4 | -------------------------------------------------------------------------------- /requirements/tools.txt: -------------------------------------------------------------------------------- 1 | mypy==1.16.0 2 | flake8==7.2.0 3 | black==24.8.0 # Until we drop Python 3.6 support, we have to stay with this version 4 | -------------------------------------------------------------------------------- /scripts/build_pypi_package.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | script_dir=`dirname $0` 4 | cd ${script_dir}/.. 5 | rm -rf ./slack_bolt.egg-info 6 | 7 | pip install -U pip && \ 8 | pip install twine build && \ 9 | rm -rf dist/ build/ slack_bolt.egg-info/ && \ 10 | python -m build --sdist --wheel && \ 11 | twine check dist/* 12 | -------------------------------------------------------------------------------- /scripts/deploy_to_pypi_org.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | script_dir=`dirname $0` 4 | cd ${script_dir}/.. 5 | rm -rf ./slack_bolt.egg-info 6 | 7 | pip install -U pip && \ 8 | pip install twine build && \ 9 | rm -rf dist/ build/ slack_bolt.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_bolt.egg-info 6 | 7 | pip install -U pip && \ 8 | pip install twine build && \ 9 | rm -rf dist/ build/ slack_bolt.egg-info/ && \ 10 | python -m build --sdist --wheel && \ 11 | twine check dist/* && \ 12 | twine upload --repository testpypi dist/* -------------------------------------------------------------------------------- /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 pdoc3 8 | rm -rf docs/static/api-docs 9 | pdoc slack_bolt --html -o docs/static/api-docs 10 | open docs/static/api-docs/slack_bolt/index.html 11 | -------------------------------------------------------------------------------- /scripts/run_flake8.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # ./scripts/run_flake8.sh 3 | 4 | script_dir=$(dirname $0) 5 | cd ${script_dir}/.. && \ 6 | pip install -r requirements/tools.txt && \ 7 | flake8 slack_bolt/ && flake8 examples/ 8 | -------------------------------------------------------------------------------- /scripts/run_mypy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # ./scripts/run_mypy.sh 3 | 4 | script_dir=$(dirname $0) 5 | cd ${script_dir}/.. && \ 6 | pip install . 7 | pip install -r requirements/async.txt && \ 8 | pip install -r requirements/adapter.txt && \ 9 | pip install -r requirements/tools.txt && \ 10 | mypy --config-file pyproject.toml 11 | -------------------------------------------------------------------------------- /scripts/run_tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Run all the tests or a single test 3 | # all: ./scripts/run_tests.sh 4 | # single: ./scripts/run_tests.sh tests/scenario_tests/test_app.py 5 | 6 | script_dir=`dirname $0` 7 | cd ${script_dir}/.. 8 | 9 | test_target="$1" 10 | python_version=`python --version | awk '{print $2}'` 11 | 12 | if [[ $test_target != "" ]] 13 | then 14 | black slack_bolt/ tests/ && \ 15 | pytest -vv $1 16 | else 17 | black slack_bolt/ tests/ && pytest 18 | fi 19 | -------------------------------------------------------------------------------- /scripts/uninstall_all.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | pip uninstall -y slack-bolt && \ 4 | pip freeze | grep -v "^-e" | xargs pip uninstall -y 5 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | ; Legacy package configuration, prefer pyproject.toml over setup.cfg 2 | [metadata] 3 | url=https://github.com/slackapi/bolt-python 4 | author=Slack Technologies, LLC 5 | author_email=opensource@slack.com 6 | -------------------------------------------------------------------------------- /slack_bolt/adapter/__init__.py: -------------------------------------------------------------------------------- 1 | """Adapter modules for running Bolt apps along with Web frameworks or Socket Mode. 2 | """ 3 | -------------------------------------------------------------------------------- /slack_bolt/adapter/asgi/__init__.py: -------------------------------------------------------------------------------- 1 | from .builtin import SlackRequestHandler 2 | 3 | __all__ = ["SlackRequestHandler"] 4 | -------------------------------------------------------------------------------- /slack_bolt/adapter/asgi/async_handler.py: -------------------------------------------------------------------------------- 1 | from .aiohttp import AsyncSlackRequestHandler 2 | 3 | __all__ = ["AsyncSlackRequestHandler"] 4 | -------------------------------------------------------------------------------- /slack_bolt/adapter/asgi/http_request.py: -------------------------------------------------------------------------------- 1 | from typing import Callable, Dict, Iterable, Sequence, Tuple, Union 2 | 3 | from .utils import scope_type, ENCODING 4 | 5 | 6 | class AsgiHttpRequest: 7 | __slots__ = ("receive", "query_string", "raw_headers") 8 | 9 | def __init__(self, scope: scope_type, receive: Callable): 10 | self.receive = receive 11 | self.query_string = str(scope["query_string"], ENCODING) # type: ignore[arg-type] 12 | self.raw_headers: Iterable[Tuple[bytes, bytes]] = scope["headers"] # type: ignore[assignment] 13 | 14 | def get_headers(self) -> Dict[str, Union[str, Sequence[str]]]: 15 | return {str(header[0], ENCODING): str(header[1], (ENCODING)) for header in self.raw_headers} 16 | 17 | async def get_raw_body(self) -> str: 18 | chunks = bytearray() 19 | while True: 20 | chunk: Dict[str, Union[str, bytes]] = await self.receive() 21 | 22 | if chunk["type"] != "http.request": 23 | raise Exception("Body chunks could not be received from asgi server") 24 | 25 | chunks.extend(chunk.get("body", b"")) # type: ignore[arg-type] 26 | if not chunk.get("more_body", False): 27 | break 28 | return bytes(chunks).decode(ENCODING) 29 | -------------------------------------------------------------------------------- /slack_bolt/adapter/asgi/http_response.py: -------------------------------------------------------------------------------- 1 | from typing import Iterable, Sequence, Tuple, Dict, Union, List 2 | 3 | from .utils import ENCODING 4 | 5 | 6 | class AsgiHttpResponse: 7 | __slots__ = ("status", "raw_headers", "body") 8 | 9 | def __init__(self, status: int, headers: Dict[str, Sequence[str]] = {}, body: str = ""): 10 | self.status: int = status 11 | self.raw_headers: List[Tuple[bytes, bytes]] = [ 12 | (bytes(key, ENCODING), bytes(value[0], ENCODING)) for key, value in headers.items() 13 | ] 14 | self.raw_headers.append((b"content-length", bytes(str(len(body)), ENCODING))) 15 | self.body: bytes = bytes(body, ENCODING) 16 | 17 | def get_response_start(self) -> Dict[str, Union[str, int, Iterable[Tuple[bytes, bytes]]]]: 18 | return { 19 | "type": "http.response.start", 20 | "status": self.status, 21 | "headers": self.raw_headers, 22 | } 23 | 24 | def get_response_body(self) -> Dict[str, Union[str, bytes, bool]]: 25 | return { 26 | "type": "http.response.body", 27 | "body": self.body, 28 | "more_body": False, 29 | } 30 | -------------------------------------------------------------------------------- /slack_bolt/adapter/asgi/utils.py: -------------------------------------------------------------------------------- 1 | from typing import Iterable, Tuple, Union, Dict 2 | 3 | ENCODING = "utf-8" # should always be utf-8 4 | 5 | scope_value_type = Union[str, bytes, Iterable[Tuple[bytes, bytes]]] 6 | 7 | scope_type = Dict[str, scope_value_type] 8 | -------------------------------------------------------------------------------- /slack_bolt/adapter/aws_lambda/__init__.py: -------------------------------------------------------------------------------- 1 | from .handler import SlackRequestHandler 2 | 3 | __all__ = [ 4 | "SlackRequestHandler", 5 | ] 6 | -------------------------------------------------------------------------------- /slack_bolt/adapter/aws_lambda/internals.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, Optional, Sequence 2 | 3 | 4 | def _first_value(query: Dict[str, Sequence[str]], name: str) -> Optional[str]: 5 | if query: 6 | values = query.get(name, []) 7 | if values and len(values) > 0: 8 | return values[0] 9 | return None 10 | -------------------------------------------------------------------------------- /slack_bolt/adapter/aws_lambda/lazy_listener_runner.py: -------------------------------------------------------------------------------- 1 | import json 2 | from logging import Logger 3 | from typing import Callable, Optional, Any 4 | 5 | import boto3 # type: ignore[import-untyped] 6 | 7 | from slack_bolt import BoltRequest 8 | from slack_bolt.lazy_listener import LazyListenerRunner 9 | 10 | 11 | class LambdaLazyListenerRunner(LazyListenerRunner): 12 | def __init__(self, logger: Logger, lambda_client: Optional[Any] = None): 13 | self.lambda_client = lambda_client 14 | self.logger = logger 15 | 16 | def start(self, function: Callable[..., None], request: BoltRequest) -> None: 17 | if self.lambda_client is None: 18 | self.lambda_client = boto3.client("lambda") 19 | 20 | event: dict = request.context["lambda_request"] 21 | headers = event["headers"] 22 | headers["x-slack-bolt-lazy-only"] = "1" # not an array 23 | headers["x-slack-bolt-lazy-function-name"] = request.lazy_function_name # not an array 24 | event["method"] = "NONE" 25 | invocation = self.lambda_client.invoke( 26 | FunctionName=request.context["aws_lambda_invoked_function_arn"], 27 | InvocationType="Event", 28 | Payload=json.dumps(event), 29 | ) 30 | self.logger.info(invocation) 31 | -------------------------------------------------------------------------------- /slack_bolt/adapter/aws_lambda/local_lambda_client.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from chalice.app import Chalice 4 | from chalice.config import Config 5 | from chalice.test import BaseClient, LambdaContext, InvokeResponse 6 | 7 | 8 | class LocalLambdaClient(BaseClient): 9 | """Lambda client implementing `invoke` for use when running with Chalice CLI.""" 10 | 11 | def __init__(self, app: Chalice, config: Config) -> None: 12 | self._app = app 13 | self._config = config if config else Config() 14 | 15 | def invoke( 16 | self, 17 | FunctionName: str, 18 | InvocationType: str = "Event", 19 | Payload: str = "{}", 20 | ) -> InvokeResponse: 21 | scoped = self._config.scope(self._config.chalice_stage, FunctionName) 22 | lambda_context = LambdaContext(FunctionName, memory_size=scoped.lambda_memory_size) 23 | 24 | with self._patched_env_vars(scoped.environment_variables): 25 | response = self._app(json.loads(Payload), lambda_context) 26 | return InvokeResponse(payload=response) 27 | -------------------------------------------------------------------------------- /slack_bolt/adapter/bottle/__init__.py: -------------------------------------------------------------------------------- 1 | from .handler import SlackRequestHandler 2 | 3 | __all__ = [ 4 | "SlackRequestHandler", 5 | ] 6 | -------------------------------------------------------------------------------- /slack_bolt/adapter/cherrypy/__init__.py: -------------------------------------------------------------------------------- 1 | from .handler import SlackRequestHandler 2 | 3 | __all__ = [ 4 | "SlackRequestHandler", 5 | ] 6 | -------------------------------------------------------------------------------- /slack_bolt/adapter/django/__init__.py: -------------------------------------------------------------------------------- 1 | from .handler import SlackRequestHandler 2 | 3 | __all__ = [ 4 | "SlackRequestHandler", 5 | ] 6 | -------------------------------------------------------------------------------- /slack_bolt/adapter/falcon/__init__.py: -------------------------------------------------------------------------------- 1 | # Don't add async module imports here 2 | from .resource import SlackAppResource 3 | 4 | __all__ = [ 5 | "SlackAppResource", 6 | ] 7 | -------------------------------------------------------------------------------- /slack_bolt/adapter/fastapi/__init__.py: -------------------------------------------------------------------------------- 1 | # Don't add async module imports here 2 | from ..starlette.handler import SlackRequestHandler 3 | 4 | __all__ = [ 5 | "SlackRequestHandler", 6 | ] 7 | -------------------------------------------------------------------------------- /slack_bolt/adapter/fastapi/async_handler.py: -------------------------------------------------------------------------------- 1 | from ..starlette.async_handler import AsyncSlackRequestHandler 2 | 3 | __all__ = [ 4 | "AsyncSlackRequestHandler", 5 | ] 6 | -------------------------------------------------------------------------------- /slack_bolt/adapter/flask/__init__.py: -------------------------------------------------------------------------------- 1 | from .handler import SlackRequestHandler 2 | 3 | __all__ = [ 4 | "SlackRequestHandler", 5 | ] 6 | -------------------------------------------------------------------------------- /slack_bolt/adapter/google_cloud_functions/__init__.py: -------------------------------------------------------------------------------- 1 | from .handler import SlackRequestHandler 2 | 3 | __all__ = [ 4 | "SlackRequestHandler", 5 | ] 6 | -------------------------------------------------------------------------------- /slack_bolt/adapter/pyramid/__init__.py: -------------------------------------------------------------------------------- 1 | from .handler import SlackRequestHandler 2 | 3 | __all__ = [ 4 | "SlackRequestHandler", 5 | ] 6 | -------------------------------------------------------------------------------- /slack_bolt/adapter/sanic/__init__.py: -------------------------------------------------------------------------------- 1 | from .async_handler import AsyncSlackRequestHandler 2 | 3 | __all__ = [ 4 | "AsyncSlackRequestHandler", 5 | ] 6 | -------------------------------------------------------------------------------- /slack_bolt/adapter/socket_mode/__init__.py: -------------------------------------------------------------------------------- 1 | """Socket Mode adapter package provides the following implementations. If you don't have strong reasons to use 3rd party library based adapters, we recommend using the built-in client based one. 2 | 3 | * `slack_bolt.adapter.socket_mode.builtin` 4 | * `slack_bolt.adapter.socket_mode.websocket_client` 5 | * `slack_bolt.adapter.socket_mode.aiohttp` 6 | * `slack_bolt.adapter.socket_mode.websockets` 7 | """ # noqa: E501 8 | 9 | # Don't add async module imports here 10 | from .builtin import SocketModeHandler 11 | 12 | __all__ = [ 13 | "SocketModeHandler", 14 | ] 15 | -------------------------------------------------------------------------------- /slack_bolt/adapter/socket_mode/async_handler.py: -------------------------------------------------------------------------------- 1 | """Default implementation is the aiohttp-based one.""" 2 | 3 | from .aiohttp import AsyncSocketModeHandler 4 | 5 | __all__ = [ 6 | "AsyncSocketModeHandler", 7 | ] 8 | -------------------------------------------------------------------------------- /slack_bolt/adapter/starlette/__init__.py: -------------------------------------------------------------------------------- 1 | # Don't add async module imports here 2 | from .handler import SlackRequestHandler 3 | 4 | __all__ = [ 5 | "SlackRequestHandler", 6 | ] 7 | -------------------------------------------------------------------------------- /slack_bolt/adapter/tornado/__init__.py: -------------------------------------------------------------------------------- 1 | # Don't add async module imports here 2 | from .handler import SlackEventsHandler, SlackOAuthHandler 3 | 4 | __all__ = [ 5 | "SlackEventsHandler", 6 | "SlackOAuthHandler", 7 | ] 8 | -------------------------------------------------------------------------------- /slack_bolt/adapter/wsgi/__init__.py: -------------------------------------------------------------------------------- 1 | from .handler import SlackRequestHandler 2 | 3 | __all__ = ["SlackRequestHandler"] 4 | -------------------------------------------------------------------------------- /slack_bolt/adapter/wsgi/http_response.py: -------------------------------------------------------------------------------- 1 | from http import HTTPStatus 2 | from typing import Dict, Iterable, List, Sequence, Tuple 3 | 4 | from .internals import ENCODING 5 | 6 | 7 | class WsgiHttpResponse: 8 | """This Class uses the PEP 3333 standard to adapt bolt response information 9 | for the WSGI web server running the application 10 | 11 | PEP 3333: https://peps.python.org/pep-3333/ 12 | """ 13 | 14 | __slots__ = ("status", "_headers", "_body") 15 | 16 | def __init__(self, status: int, headers: Dict[str, Sequence[str]] = {}, body: str = ""): 17 | _status = HTTPStatus(status) 18 | self.status = f"{_status.value} {_status.phrase}" 19 | self._headers = headers 20 | self._body = bytes(body, ENCODING) 21 | 22 | def get_headers(self) -> List[Tuple[str, str]]: 23 | headers: List[Tuple[str, str]] = [] 24 | for key, value in self._headers.items(): 25 | if key.lower() == "content-length": 26 | continue 27 | headers.append((key, value[0])) 28 | 29 | headers.append(("content-length", str(len(self._body)))) 30 | return headers 31 | 32 | def get_body(self) -> Iterable[bytes]: 33 | return [self._body] 34 | -------------------------------------------------------------------------------- /slack_bolt/adapter/wsgi/internals.py: -------------------------------------------------------------------------------- 1 | ENCODING = "utf-8" # The content encoding for Slack requests/responses is always utf-8 2 | -------------------------------------------------------------------------------- /slack_bolt/app/__init__.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | """Application interface in Bolt. 3 | 4 | For most use cases, we recommend using `slack_bolt.app.app`. 5 | If you already have knowledge about asyncio and prefer the programming model, 6 | you can use `slack_bolt.app.async_app` for building async apps.\ 7 | """ 8 | 9 | # Don't add async module imports here 10 | from .app import App 11 | 12 | __all__ = [ 13 | "App", 14 | ] 15 | -------------------------------------------------------------------------------- /slack_bolt/authorization/__init__.py: -------------------------------------------------------------------------------- 1 | """Authorization is the process of determining which Slack credentials should be available 2 | while processing an incoming Slack event. 3 | 4 | Refer to https://slack.dev/bolt-python/concepts#authorization for details. 5 | """ 6 | 7 | from .authorize_result import AuthorizeResult 8 | 9 | __all__ = [ 10 | "AuthorizeResult", 11 | ] 12 | -------------------------------------------------------------------------------- /slack_bolt/authorization/async_authorize_args.py: -------------------------------------------------------------------------------- 1 | from logging import Logger 2 | from typing import Optional 3 | 4 | from slack_sdk.web.async_client import AsyncWebClient 5 | 6 | from slack_bolt.context.async_context import AsyncBoltContext 7 | 8 | 9 | class AsyncAuthorizeArgs: 10 | context: AsyncBoltContext 11 | logger: Logger 12 | client: AsyncWebClient 13 | enterprise_id: Optional[str] 14 | team_id: Optional[str] 15 | user_id: Optional[str] 16 | 17 | def __init__( 18 | self, 19 | *, 20 | context: AsyncBoltContext, 21 | enterprise_id: Optional[str], 22 | team_id: Optional[str], # can be None for org-wide installed apps 23 | user_id: Optional[str], 24 | ): 25 | """The full list of the arguments passed to `authorize` function. 26 | 27 | Args: 28 | context: The request context 29 | enterprise_id: The Organization ID (Enterprise Grid) 30 | team_id: The workspace ID 31 | user_id: The request user ID 32 | """ 33 | self.context = context 34 | self.logger = context.logger 35 | self.client = context.client 36 | self.enterprise_id = enterprise_id 37 | self.team_id = team_id 38 | self.user_id = user_id 39 | -------------------------------------------------------------------------------- /slack_bolt/authorization/authorize_args.py: -------------------------------------------------------------------------------- 1 | from logging import Logger 2 | from typing import Optional 3 | 4 | from slack_sdk.web import WebClient 5 | 6 | from slack_bolt.context.context import BoltContext 7 | 8 | 9 | class AuthorizeArgs: 10 | context: BoltContext 11 | logger: Logger 12 | client: WebClient 13 | enterprise_id: Optional[str] 14 | team_id: Optional[str] 15 | user_id: Optional[str] 16 | 17 | def __init__( 18 | self, 19 | *, 20 | context: BoltContext, 21 | enterprise_id: Optional[str], 22 | team_id: Optional[str], # can be None for org-wide installed apps 23 | user_id: Optional[str], 24 | ): 25 | """The full list of the arguments passed to `authorize` function. 26 | 27 | Args: 28 | context: The request context 29 | enterprise_id: The Organization ID (Enterprise Grid) 30 | team_id: The workspace ID 31 | user_id: The request user ID 32 | """ 33 | self.context = context 34 | self.logger = context.logger 35 | self.client = context.client 36 | self.enterprise_id = enterprise_id 37 | self.team_id = team_id 38 | self.user_id = user_id 39 | -------------------------------------------------------------------------------- /slack_bolt/context/__init__.py: -------------------------------------------------------------------------------- 1 | """All listeners have access to a context dictionary, which can be used to enrich events with additional information. 2 | Bolt automatically attaches information that is included in the incoming event, 3 | like `user_id`, `team_id`, `channel_id`, and `enterprise_id`. 4 | 5 | Refer to https://slack.dev/bolt-python/concepts#context for details. 6 | """ 7 | 8 | # Don't add async module imports here 9 | from .context import BoltContext 10 | 11 | __all__ = [ 12 | "BoltContext", 13 | ] 14 | -------------------------------------------------------------------------------- /slack_bolt/context/ack/__init__.py: -------------------------------------------------------------------------------- 1 | # Don't add async module imports here 2 | from .ack import Ack 3 | 4 | __all__ = [ 5 | "Ack", 6 | ] 7 | -------------------------------------------------------------------------------- /slack_bolt/context/assistant/__init__.py: -------------------------------------------------------------------------------- 1 | # Don't add async module imports here 2 | -------------------------------------------------------------------------------- /slack_bolt/context/assistant/internals.py: -------------------------------------------------------------------------------- 1 | def has_channel_id_and_thread_ts(payload: dict) -> bool: 2 | """Verifies if the given payload has both channel_id and thread_ts under assistant_thread property. 3 | This data pattern is available for assistant_* events. 4 | """ 5 | return ( 6 | payload.get("assistant_thread") is not None 7 | and payload["assistant_thread"].get("channel_id") is not None 8 | and payload["assistant_thread"].get("thread_ts") is not None 9 | ) 10 | -------------------------------------------------------------------------------- /slack_bolt/context/assistant/thread_context/__init__.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | 4 | class AssistantThreadContext(dict): 5 | enterprise_id: Optional[str] 6 | team_id: Optional[str] 7 | channel_id: str 8 | 9 | def __init__(self, payload: dict): 10 | dict.__init__(self, **payload) 11 | self.enterprise_id = payload.get("enterprise_id") 12 | self.team_id = payload.get("team_id") 13 | self.channel_id = payload["channel_id"] 14 | -------------------------------------------------------------------------------- /slack_bolt/context/assistant/thread_context_store/__init__.py: -------------------------------------------------------------------------------- 1 | # Don't add async module imports here 2 | -------------------------------------------------------------------------------- /slack_bolt/context/assistant/thread_context_store/async_store.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, Optional 2 | 3 | from slack_bolt.context.assistant.thread_context import AssistantThreadContext 4 | 5 | 6 | class AsyncAssistantThreadContextStore: 7 | async def save(self, *, channel_id: str, thread_ts: str, context: Dict[str, str]) -> None: 8 | raise NotImplementedError() 9 | 10 | async def find(self, *, channel_id: str, thread_ts: str) -> Optional[AssistantThreadContext]: 11 | raise NotImplementedError() 12 | -------------------------------------------------------------------------------- /slack_bolt/context/assistant/thread_context_store/store.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, Optional 2 | 3 | from slack_bolt.context.assistant.thread_context import AssistantThreadContext 4 | 5 | 6 | class AssistantThreadContextStore: 7 | def save(self, *, channel_id: str, thread_ts: str, context: Dict[str, str]) -> None: 8 | raise NotImplementedError() 9 | 10 | def find(self, *, channel_id: str, thread_ts: str) -> Optional[AssistantThreadContext]: 11 | raise NotImplementedError() 12 | -------------------------------------------------------------------------------- /slack_bolt/context/complete/__init__.py: -------------------------------------------------------------------------------- 1 | # Don't add async module imports here 2 | from .complete import Complete 3 | 4 | __all__ = [ 5 | "Complete", 6 | ] 7 | -------------------------------------------------------------------------------- /slack_bolt/context/complete/complete.py: -------------------------------------------------------------------------------- 1 | from typing import Any, Dict, Optional 2 | 3 | from slack_sdk import WebClient 4 | from slack_sdk.web import SlackResponse 5 | 6 | 7 | class Complete: 8 | client: WebClient 9 | function_execution_id: Optional[str] 10 | 11 | def __init__( 12 | self, 13 | client: WebClient, 14 | function_execution_id: Optional[str], 15 | ): 16 | self.client = client 17 | self.function_execution_id = function_execution_id 18 | 19 | def __call__(self, outputs: Optional[Dict[str, Any]] = None) -> SlackResponse: 20 | """Signal the successful completion of the custom function. 21 | 22 | Kwargs: 23 | outputs: Json serializable object containing the output values 24 | 25 | Returns: 26 | SlackResponse: The response object returned from slack 27 | 28 | Raises: 29 | ValueError: If this function cannot be used. 30 | """ 31 | if self.function_execution_id is None: 32 | raise ValueError("complete is unsupported here as there is no function_execution_id") 33 | 34 | return self.client.functions_completeSuccess(function_execution_id=self.function_execution_id, outputs=outputs or {}) 35 | -------------------------------------------------------------------------------- /slack_bolt/context/fail/__init__.py: -------------------------------------------------------------------------------- 1 | # Don't add async module imports here 2 | from .fail import Fail 3 | 4 | __all__ = [ 5 | "Fail", 6 | ] 7 | -------------------------------------------------------------------------------- /slack_bolt/context/fail/async_fail.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | from slack_sdk.web.async_client import AsyncWebClient 4 | from slack_sdk.web.async_slack_response import AsyncSlackResponse 5 | 6 | 7 | class AsyncFail: 8 | client: AsyncWebClient 9 | function_execution_id: Optional[str] 10 | 11 | def __init__( 12 | self, 13 | client: AsyncWebClient, 14 | function_execution_id: Optional[str], 15 | ): 16 | self.client = client 17 | self.function_execution_id = function_execution_id 18 | 19 | async def __call__(self, error: str) -> AsyncSlackResponse: 20 | """Signal that the custom function failed to complete. 21 | 22 | Kwargs: 23 | error: Error message to return to slack 24 | 25 | Returns: 26 | SlackResponse: The response object returned from slack 27 | 28 | Raises: 29 | ValueError: If this function cannot be used. 30 | """ 31 | if self.function_execution_id is None: 32 | raise ValueError("fail is unsupported here as there is no function_execution_id") 33 | 34 | return await self.client.functions_completeError(function_execution_id=self.function_execution_id, error=error) 35 | -------------------------------------------------------------------------------- /slack_bolt/context/fail/fail.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | from slack_sdk import WebClient 4 | from slack_sdk.web import SlackResponse 5 | 6 | 7 | class Fail: 8 | client: WebClient 9 | function_execution_id: Optional[str] 10 | 11 | def __init__( 12 | self, 13 | client: WebClient, 14 | function_execution_id: Optional[str], 15 | ): 16 | self.client = client 17 | self.function_execution_id = function_execution_id 18 | 19 | def __call__(self, error: str) -> SlackResponse: 20 | """Signal that the custom function failed to complete. 21 | 22 | Kwargs: 23 | error: Error message to return to slack 24 | 25 | Returns: 26 | SlackResponse: The response object returned from slack 27 | 28 | Raises: 29 | ValueError: If this function cannot be used. 30 | """ 31 | if self.function_execution_id is None: 32 | raise ValueError("fail is unsupported here as there is no function_execution_id") 33 | 34 | return self.client.functions_completeError(function_execution_id=self.function_execution_id, error=error) 35 | -------------------------------------------------------------------------------- /slack_bolt/context/get_thread_context/__init__.py: -------------------------------------------------------------------------------- 1 | # Don't add async module imports here 2 | from .get_thread_context import GetThreadContext 3 | 4 | __all__ = [ 5 | "GetThreadContext", 6 | ] 7 | -------------------------------------------------------------------------------- /slack_bolt/context/respond/__init__.py: -------------------------------------------------------------------------------- 1 | # Don't add async module imports here 2 | from .respond import Respond 3 | 4 | __all__ = [ 5 | "Respond", 6 | ] 7 | -------------------------------------------------------------------------------- /slack_bolt/context/save_thread_context/__init__.py: -------------------------------------------------------------------------------- 1 | # Don't add async module imports here 2 | from .save_thread_context import SaveThreadContext 3 | 4 | __all__ = [ 5 | "SaveThreadContext", 6 | ] 7 | -------------------------------------------------------------------------------- /slack_bolt/context/save_thread_context/async_save_thread_context.py: -------------------------------------------------------------------------------- 1 | from typing import Dict 2 | 3 | from slack_bolt.context.assistant.thread_context_store.async_store import AsyncAssistantThreadContextStore 4 | 5 | 6 | class AsyncSaveThreadContext: 7 | thread_context_store: AsyncAssistantThreadContextStore 8 | channel_id: str 9 | thread_ts: str 10 | 11 | def __init__( 12 | self, 13 | thread_context_store: AsyncAssistantThreadContextStore, 14 | channel_id: str, 15 | thread_ts: str, 16 | ): 17 | self.thread_context_store = thread_context_store 18 | self.channel_id = channel_id 19 | self.thread_ts = thread_ts 20 | 21 | async def __call__(self, new_context: Dict[str, str]) -> None: 22 | await self.thread_context_store.save( 23 | channel_id=self.channel_id, 24 | thread_ts=self.thread_ts, 25 | context=new_context, 26 | ) 27 | -------------------------------------------------------------------------------- /slack_bolt/context/save_thread_context/save_thread_context.py: -------------------------------------------------------------------------------- 1 | from typing import Dict 2 | 3 | from slack_bolt.context.assistant.thread_context_store.store import AssistantThreadContextStore 4 | 5 | 6 | class SaveThreadContext: 7 | thread_context_store: AssistantThreadContextStore 8 | channel_id: str 9 | thread_ts: str 10 | 11 | def __init__( 12 | self, 13 | thread_context_store: AssistantThreadContextStore, 14 | channel_id: str, 15 | thread_ts: str, 16 | ): 17 | self.thread_context_store = thread_context_store 18 | self.channel_id = channel_id 19 | self.thread_ts = thread_ts 20 | 21 | def __call__(self, new_context: Dict[str, str]) -> None: 22 | self.thread_context_store.save( 23 | channel_id=self.channel_id, 24 | thread_ts=self.thread_ts, 25 | context=new_context, 26 | ) 27 | -------------------------------------------------------------------------------- /slack_bolt/context/say/__init__.py: -------------------------------------------------------------------------------- 1 | # Don't add async module imports here 2 | from .say import Say 3 | 4 | __all__ = [ 5 | "Say", 6 | ] 7 | -------------------------------------------------------------------------------- /slack_bolt/context/say/internals.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Any 2 | 3 | 4 | def _can_say(self: Any, channel: Optional[str]) -> bool: 5 | return hasattr(self, "client") and self.client is not None and (channel or self.channel) is not None 6 | -------------------------------------------------------------------------------- /slack_bolt/context/set_status/__init__.py: -------------------------------------------------------------------------------- 1 | # Don't add async module imports here 2 | from .set_status import SetStatus 3 | 4 | __all__ = [ 5 | "SetStatus", 6 | ] 7 | -------------------------------------------------------------------------------- /slack_bolt/context/set_status/async_set_status.py: -------------------------------------------------------------------------------- 1 | from slack_sdk.web.async_client import AsyncWebClient 2 | from slack_sdk.web.async_slack_response import AsyncSlackResponse 3 | 4 | 5 | class AsyncSetStatus: 6 | client: AsyncWebClient 7 | channel_id: str 8 | thread_ts: str 9 | 10 | def __init__( 11 | self, 12 | client: AsyncWebClient, 13 | channel_id: str, 14 | thread_ts: str, 15 | ): 16 | self.client = client 17 | self.channel_id = channel_id 18 | self.thread_ts = thread_ts 19 | 20 | async def __call__(self, status: str) -> AsyncSlackResponse: 21 | return await self.client.assistant_threads_setStatus( 22 | status=status, 23 | channel_id=self.channel_id, 24 | thread_ts=self.thread_ts, 25 | ) 26 | -------------------------------------------------------------------------------- /slack_bolt/context/set_status/set_status.py: -------------------------------------------------------------------------------- 1 | from slack_sdk import WebClient 2 | from slack_sdk.web import SlackResponse 3 | 4 | 5 | class SetStatus: 6 | client: WebClient 7 | channel_id: str 8 | thread_ts: str 9 | 10 | def __init__( 11 | self, 12 | client: WebClient, 13 | channel_id: str, 14 | thread_ts: str, 15 | ): 16 | self.client = client 17 | self.channel_id = channel_id 18 | self.thread_ts = thread_ts 19 | 20 | def __call__(self, status: str) -> SlackResponse: 21 | return self.client.assistant_threads_setStatus( 22 | status=status, 23 | channel_id=self.channel_id, 24 | thread_ts=self.thread_ts, 25 | ) 26 | -------------------------------------------------------------------------------- /slack_bolt/context/set_suggested_prompts/__init__.py: -------------------------------------------------------------------------------- 1 | # Don't add async module imports here 2 | from .set_suggested_prompts import SetSuggestedPrompts 3 | 4 | __all__ = [ 5 | "SetSuggestedPrompts", 6 | ] 7 | -------------------------------------------------------------------------------- /slack_bolt/context/set_suggested_prompts/set_suggested_prompts.py: -------------------------------------------------------------------------------- 1 | from typing import List, Dict, Union, Optional 2 | 3 | from slack_sdk import WebClient 4 | from slack_sdk.web import SlackResponse 5 | 6 | 7 | class SetSuggestedPrompts: 8 | client: WebClient 9 | channel_id: str 10 | thread_ts: str 11 | 12 | def __init__( 13 | self, 14 | client: WebClient, 15 | channel_id: str, 16 | thread_ts: str, 17 | ): 18 | self.client = client 19 | self.channel_id = channel_id 20 | self.thread_ts = thread_ts 21 | 22 | def __call__( 23 | self, 24 | prompts: List[Union[str, Dict[str, str]]], 25 | title: Optional[str] = None, 26 | ) -> SlackResponse: 27 | prompts_arg: List[Dict[str, str]] = [] 28 | for prompt in prompts: 29 | if isinstance(prompt, str): 30 | prompts_arg.append({"title": prompt, "message": prompt}) 31 | else: 32 | prompts_arg.append(prompt) 33 | 34 | return self.client.assistant_threads_setSuggestedPrompts( 35 | channel_id=self.channel_id, 36 | thread_ts=self.thread_ts, 37 | prompts=prompts_arg, 38 | title=title, 39 | ) 40 | -------------------------------------------------------------------------------- /slack_bolt/context/set_title/__init__.py: -------------------------------------------------------------------------------- 1 | # Don't add async module imports here 2 | from .set_title import SetTitle 3 | 4 | __all__ = [ 5 | "SetTitle", 6 | ] 7 | -------------------------------------------------------------------------------- /slack_bolt/context/set_title/async_set_title.py: -------------------------------------------------------------------------------- 1 | from slack_sdk.web.async_client import AsyncWebClient 2 | from slack_sdk.web.async_slack_response import AsyncSlackResponse 3 | 4 | 5 | class AsyncSetTitle: 6 | client: AsyncWebClient 7 | channel_id: str 8 | thread_ts: str 9 | 10 | def __init__( 11 | self, 12 | client: AsyncWebClient, 13 | channel_id: str, 14 | thread_ts: str, 15 | ): 16 | self.client = client 17 | self.channel_id = channel_id 18 | self.thread_ts = thread_ts 19 | 20 | async def __call__(self, title: str) -> AsyncSlackResponse: 21 | return await self.client.assistant_threads_setTitle( 22 | title=title, 23 | channel_id=self.channel_id, 24 | thread_ts=self.thread_ts, 25 | ) 26 | -------------------------------------------------------------------------------- /slack_bolt/context/set_title/set_title.py: -------------------------------------------------------------------------------- 1 | from slack_sdk import WebClient 2 | from slack_sdk.web import SlackResponse 3 | 4 | 5 | class SetTitle: 6 | client: WebClient 7 | channel_id: str 8 | thread_ts: str 9 | 10 | def __init__( 11 | self, 12 | client: WebClient, 13 | channel_id: str, 14 | thread_ts: str, 15 | ): 16 | self.client = client 17 | self.channel_id = channel_id 18 | self.thread_ts = thread_ts 19 | 20 | def __call__(self, title: str) -> SlackResponse: 21 | return self.client.assistant_threads_setTitle( 22 | title=title, 23 | channel_id=self.channel_id, 24 | thread_ts=self.thread_ts, 25 | ) 26 | -------------------------------------------------------------------------------- /slack_bolt/error/__init__.py: -------------------------------------------------------------------------------- 1 | """Bolt specific error types.""" 2 | 3 | from typing import Optional, Union 4 | 5 | 6 | class BoltError(Exception): 7 | """General class in a Bolt app""" 8 | 9 | 10 | class BoltUnhandledRequestError(BoltError): 11 | request: "BoltRequest" # type: ignore[name-defined] 12 | body: dict 13 | current_response: Optional["BoltResponse"] # type: ignore[name-defined] 14 | last_global_middleware_name: Optional[str] 15 | 16 | def __init__( 17 | self, 18 | *, 19 | request: Union["BoltRequest", "AsyncBoltRequest"], # type: ignore[name-defined] 20 | current_response: Optional["BoltResponse"], # type: ignore[name-defined] 21 | last_global_middleware_name: Optional[str] = None, 22 | ): 23 | self.request = request 24 | self.body = request.body if request is not None else {} 25 | self.current_response = current_response 26 | self.last_global_middleware_name = last_global_middleware_name 27 | 28 | def __str__(self) -> str: 29 | return "unhandled request error" 30 | -------------------------------------------------------------------------------- /slack_bolt/kwargs_injection/__init__.py: -------------------------------------------------------------------------------- 1 | """For middleware/listener arguments, Bolt does flexible data injection in accordance with their names. 2 | 3 | To learn the available arguments, check `slack_bolt.kwargs_injection.args`'s API document. 4 | For steps from apps, checking `slack_bolt.workflows.step.utilities` as well should be helpful. 5 | """ 6 | 7 | # Don't add async module imports here 8 | from .args import Args 9 | from .utils import build_required_kwargs 10 | 11 | __all__ = [ 12 | "Args", 13 | "build_required_kwargs", 14 | ] 15 | -------------------------------------------------------------------------------- /slack_bolt/lazy_listener/__init__.py: -------------------------------------------------------------------------------- 1 | """Lazy listener runner is a beta feature for the apps running on Function-as-a-Service platforms. 2 | 3 | def respond_to_slack_within_3_seconds(body, ack): 4 | text = body.get("text") 5 | if text is None or len(text) == 0: 6 | ack(f":x: Usage: /start-process (description here)") 7 | else: 8 | ack(f"Accepted! (task: {body['text']})") 9 | 10 | import time 11 | def run_long_process(respond, body): 12 | time.sleep(5) # longer than 3 seconds 13 | respond(f"Completed! (task: {body['text']})") 14 | 15 | app.command("/start-process")( 16 | # ack() is still called within 3 seconds 17 | ack=respond_to_slack_within_3_seconds, 18 | # Lazy function is responsible for processing the event 19 | lazy=[run_long_process] 20 | ) 21 | 22 | Refer to https://slack.dev/bolt-python/concepts#lazy-listeners for more details. 23 | """ 24 | 25 | # Don't add async module imports here 26 | from .runner import LazyListenerRunner 27 | from .thread_runner import ThreadLazyListenerRunner 28 | 29 | __all__ = [ 30 | "LazyListenerRunner", 31 | "ThreadLazyListenerRunner", 32 | ] 33 | -------------------------------------------------------------------------------- /slack_bolt/lazy_listener/async_internals.py: -------------------------------------------------------------------------------- 1 | from functools import wraps 2 | from logging import Logger 3 | from typing import Callable, Awaitable 4 | 5 | from slack_bolt.kwargs_injection.async_utils import build_async_required_kwargs 6 | from slack_bolt.request.async_request import AsyncBoltRequest 7 | from slack_bolt.util.utils import get_arg_names_of_callable 8 | 9 | 10 | async def to_runnable_function( 11 | internal_func: Callable[..., Awaitable[None]], 12 | logger: Logger, 13 | request: AsyncBoltRequest, 14 | ): 15 | arg_names = get_arg_names_of_callable(internal_func) 16 | 17 | @wraps(internal_func) 18 | async def request_wired_wrapper() -> None: 19 | try: 20 | await internal_func( 21 | **build_async_required_kwargs( 22 | logger=logger, 23 | required_arg_names=arg_names, 24 | request=request, 25 | response=None, 26 | this_func=internal_func, 27 | ) 28 | ) 29 | except Exception as e: 30 | logger.error(f"Failed to run an internal function ({e})") 31 | 32 | return await request_wired_wrapper() 33 | -------------------------------------------------------------------------------- /slack_bolt/lazy_listener/asyncio_runner.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | from logging import Logger 3 | from typing import Callable, Awaitable 4 | 5 | from slack_bolt.lazy_listener.async_internals import to_runnable_function 6 | from slack_bolt.lazy_listener.async_runner import AsyncLazyListenerRunner 7 | from slack_bolt.request.async_request import AsyncBoltRequest 8 | 9 | 10 | class AsyncioLazyListenerRunner(AsyncLazyListenerRunner): 11 | logger: Logger 12 | 13 | def __init__( 14 | self, 15 | logger: Logger, 16 | ): 17 | self.logger = logger 18 | 19 | def start(self, function: Callable[..., Awaitable[None]], request: AsyncBoltRequest) -> None: 20 | asyncio.ensure_future( 21 | to_runnable_function( 22 | internal_func=function, 23 | logger=self.logger, 24 | request=request, 25 | ) 26 | ) 27 | -------------------------------------------------------------------------------- /slack_bolt/lazy_listener/internals.py: -------------------------------------------------------------------------------- 1 | from functools import wraps 2 | from logging import Logger 3 | from typing import Callable 4 | 5 | from slack_bolt.kwargs_injection import build_required_kwargs 6 | from slack_bolt.request import BoltRequest 7 | from slack_bolt.util.utils import get_arg_names_of_callable 8 | 9 | 10 | def build_runnable_function( 11 | func: Callable[..., None], 12 | logger: Logger, 13 | request: BoltRequest, 14 | ) -> Callable[[], None]: 15 | arg_names = get_arg_names_of_callable(func) 16 | 17 | @wraps(func) 18 | def request_wired_func_wrapper() -> None: 19 | try: 20 | func( 21 | **build_required_kwargs( 22 | logger=logger, 23 | required_arg_names=arg_names, 24 | request=request, 25 | response=None, 26 | this_func=func, 27 | ) 28 | ) 29 | except Exception as e: 30 | logger.error(f"Failed to run an internal function ({e})") 31 | 32 | return request_wired_func_wrapper 33 | -------------------------------------------------------------------------------- /slack_bolt/lazy_listener/runner.py: -------------------------------------------------------------------------------- 1 | from abc import abstractmethod, ABCMeta 2 | from logging import Logger 3 | from typing import Callable 4 | 5 | from slack_bolt.lazy_listener.internals import build_runnable_function 6 | from slack_bolt.request import BoltRequest 7 | 8 | 9 | class LazyListenerRunner(metaclass=ABCMeta): 10 | logger: Logger 11 | 12 | @abstractmethod 13 | def start(self, function: Callable[..., None], request: BoltRequest) -> None: 14 | """Starts a new lazy listener execution. 15 | 16 | Args: 17 | function: The function to run. 18 | request: The request to pass to the function. The object must be thread-safe. 19 | """ 20 | raise NotImplementedError() 21 | 22 | def run(self, function: Callable[..., None], request: BoltRequest) -> None: 23 | """Synchronously runs the function with a given request data. 24 | 25 | Args: 26 | function: The function to run. 27 | request: The request to pass to the function. The object must be thread-safe. 28 | """ 29 | build_runnable_function( 30 | func=function, 31 | logger=self.logger, 32 | request=request, 33 | )() 34 | -------------------------------------------------------------------------------- /slack_bolt/lazy_listener/thread_runner.py: -------------------------------------------------------------------------------- 1 | from concurrent.futures import Executor 2 | from logging import Logger 3 | from typing import Callable 4 | 5 | from slack_bolt.lazy_listener.internals import build_runnable_function 6 | from slack_bolt.lazy_listener.runner import LazyListenerRunner 7 | from slack_bolt.request import BoltRequest 8 | 9 | 10 | class ThreadLazyListenerRunner(LazyListenerRunner): 11 | logger: Logger 12 | 13 | def __init__( 14 | self, 15 | logger: Logger, 16 | executor: Executor, 17 | ): 18 | self.logger = logger 19 | self.executor = executor 20 | 21 | def start(self, function: Callable[..., None], request: BoltRequest) -> None: 22 | self.executor.submit( 23 | build_runnable_function( 24 | func=function, 25 | logger=self.logger, 26 | request=request, 27 | ) 28 | ) 29 | -------------------------------------------------------------------------------- /slack_bolt/listener/__init__.py: -------------------------------------------------------------------------------- 1 | """Listeners process an incoming request from Slack if the request's type or data structure matches 2 | the predefined conditions of the listener. Typically, a listener acknowledge requests from Slack, 3 | process the request data, and may send response back to Slack. 4 | """ 5 | 6 | # Don't add async module imports here 7 | from .custom_listener import CustomListener 8 | from .listener import Listener 9 | 10 | builtin_listener_classes = [ 11 | CustomListener, 12 | ] 13 | for cls in builtin_listener_classes: 14 | Listener.register(cls) 15 | 16 | __all__ = [ 17 | "CustomListener", 18 | "Listener", 19 | "builtin_listener_classes", 20 | ] 21 | -------------------------------------------------------------------------------- /slack_bolt/listener_matcher/__init__.py: -------------------------------------------------------------------------------- 1 | """A listener matcher is a simplified version of listener middleware. 2 | A listener matcher function returns bool value instead of `next()` method invocation inside. 3 | This interface enables developers to utilize simple predicate functions for additional listener conditions. 4 | """ 5 | 6 | # Don't add async module imports here 7 | from .custom_listener_matcher import CustomListenerMatcher 8 | from .listener_matcher import ListenerMatcher 9 | 10 | builtin_listener_matcher_classes = [ 11 | CustomListenerMatcher, 12 | ] 13 | for cls in builtin_listener_matcher_classes: 14 | ListenerMatcher.register(cls) 15 | 16 | __all__ = [ 17 | "CustomListenerMatcher", 18 | "ListenerMatcher", 19 | "builtin_listener_matcher_classes", 20 | ] 21 | -------------------------------------------------------------------------------- /slack_bolt/listener_matcher/async_builtins.py: -------------------------------------------------------------------------------- 1 | from slack_bolt.request.async_request import AsyncBoltRequest 2 | from slack_bolt.response import BoltResponse 3 | from .async_listener_matcher import AsyncListenerMatcher 4 | from .builtins import BuiltinListenerMatcher 5 | from ..kwargs_injection.async_utils import build_async_required_kwargs 6 | 7 | 8 | class AsyncBuiltinListenerMatcher(BuiltinListenerMatcher, AsyncListenerMatcher): 9 | async def async_matches(self, req: AsyncBoltRequest, resp: BoltResponse) -> bool: 10 | return await self.func( # type: ignore[misc] 11 | **build_async_required_kwargs( 12 | logger=self.logger, 13 | required_arg_names=self.arg_names, 14 | request=req, 15 | response=resp, 16 | this_func=self.func, 17 | ) 18 | ) 19 | -------------------------------------------------------------------------------- /slack_bolt/listener_matcher/listener_matcher.py: -------------------------------------------------------------------------------- 1 | from abc import abstractmethod, ABCMeta 2 | 3 | from slack_bolt.request import BoltRequest 4 | from slack_bolt.response import BoltResponse 5 | 6 | 7 | class ListenerMatcher(metaclass=ABCMeta): 8 | @abstractmethod 9 | def matches(self, req: BoltRequest, resp: BoltResponse) -> bool: 10 | """Matches against the request and returns True if matched. 11 | 12 | Args: 13 | req: The request 14 | resp: The response 15 | 16 | Returns: 17 | True if matched. 18 | """ 19 | raise NotImplementedError() 20 | -------------------------------------------------------------------------------- /slack_bolt/middleware/assistant/__init__.py: -------------------------------------------------------------------------------- 1 | # Don't add async module imports here 2 | from .assistant import Assistant 3 | 4 | __all__ = [ 5 | "Assistant", 6 | ] 7 | -------------------------------------------------------------------------------- /slack_bolt/middleware/async_builtins.py: -------------------------------------------------------------------------------- 1 | from .ignoring_self_events.async_ignoring_self_events import ( 2 | AsyncIgnoringSelfEvents, 3 | ) 4 | from .request_verification.async_request_verification import ( 5 | AsyncRequestVerification, 6 | ) 7 | from .ssl_check.async_ssl_check import AsyncSslCheck 8 | from .url_verification.async_url_verification import AsyncUrlVerification 9 | from .message_listener_matches.async_message_listener_matches import ( 10 | AsyncMessageListenerMatches, 11 | ) 12 | from .attaching_function_token.async_attaching_function_token import AsyncAttachingFunctionToken 13 | 14 | __all__ = [ 15 | "AsyncIgnoringSelfEvents", 16 | "AsyncRequestVerification", 17 | "AsyncSslCheck", 18 | "AsyncUrlVerification", 19 | "AsyncMessageListenerMatches", 20 | "AsyncAttachingFunctionToken", 21 | ] 22 | -------------------------------------------------------------------------------- /slack_bolt/middleware/attaching_function_token/__init__.py: -------------------------------------------------------------------------------- 1 | from .attaching_function_token import AttachingFunctionToken 2 | 3 | __all__ = [ 4 | "AttachingFunctionToken", 5 | ] 6 | -------------------------------------------------------------------------------- /slack_bolt/middleware/attaching_function_token/async_attaching_function_token.py: -------------------------------------------------------------------------------- 1 | from typing import Callable, Awaitable 2 | 3 | from slack_bolt.request.async_request import AsyncBoltRequest 4 | from slack_bolt.response import BoltResponse 5 | from slack_bolt.middleware.async_middleware import AsyncMiddleware 6 | 7 | 8 | class AsyncAttachingFunctionToken(AsyncMiddleware): 9 | async def async_process( 10 | self, 11 | *, 12 | req: AsyncBoltRequest, 13 | resp: BoltResponse, 14 | # This method is not supposed to be invoked by bolt-python users 15 | next: Callable[[], Awaitable[BoltResponse]], 16 | ) -> BoltResponse: 17 | if req.context.function_bot_access_token is not None: 18 | req.context.client.token = req.context.function_bot_access_token 19 | 20 | return await next() 21 | -------------------------------------------------------------------------------- /slack_bolt/middleware/attaching_function_token/attaching_function_token.py: -------------------------------------------------------------------------------- 1 | from typing import Callable 2 | 3 | from slack_bolt.request import BoltRequest 4 | from slack_bolt.response import BoltResponse 5 | from slack_bolt.middleware.middleware import Middleware 6 | 7 | 8 | class AttachingFunctionToken(Middleware): 9 | def process( 10 | self, 11 | *, 12 | req: BoltRequest, 13 | resp: BoltResponse, 14 | # This method is not supposed to be invoked by bolt-python users 15 | next: Callable[[], BoltResponse], 16 | ) -> BoltResponse: 17 | if req.context.function_bot_access_token is not None: 18 | req.context.client.token = req.context.function_bot_access_token 19 | 20 | return next() 21 | -------------------------------------------------------------------------------- /slack_bolt/middleware/authorization/__init__.py: -------------------------------------------------------------------------------- 1 | # Don't add async module imports here 2 | from .authorization import Authorization 3 | from .multi_teams_authorization import MultiTeamsAuthorization 4 | from .single_team_authorization import SingleTeamAuthorization 5 | 6 | __all__ = [ 7 | "Authorization", 8 | "MultiTeamsAuthorization", 9 | "SingleTeamAuthorization", 10 | ] 11 | -------------------------------------------------------------------------------- /slack_bolt/middleware/authorization/async_authorization.py: -------------------------------------------------------------------------------- 1 | from abc import ABC 2 | 3 | from slack_bolt.middleware.async_middleware import AsyncMiddleware 4 | 5 | 6 | class AsyncAuthorization(AsyncMiddleware, ABC): 7 | pass 8 | -------------------------------------------------------------------------------- /slack_bolt/middleware/authorization/async_internals.py: -------------------------------------------------------------------------------- 1 | from slack_bolt.request.async_request import AsyncBoltRequest 2 | from slack_bolt.response import BoltResponse 3 | 4 | 5 | def _is_url_verification(req: AsyncBoltRequest) -> bool: 6 | return req is not None and req.body is not None and req.body.get("type") == "url_verification" 7 | 8 | 9 | def _is_ssl_check(req: AsyncBoltRequest) -> bool: 10 | return req is not None and req.body is not None and req.body.get("type") == "ssl_check" 11 | 12 | 13 | def _is_no_auth_required(req: AsyncBoltRequest) -> bool: 14 | return _is_url_verification(req) or _is_ssl_check(req) 15 | 16 | 17 | def _build_user_facing_error_response(message: str) -> BoltResponse: 18 | # show an ephemeral message to the end-user 19 | return BoltResponse( 20 | status=200, 21 | body=message, 22 | ) 23 | -------------------------------------------------------------------------------- /slack_bolt/middleware/authorization/authorization.py: -------------------------------------------------------------------------------- 1 | from ..middleware import Middleware 2 | 3 | 4 | class Authorization(Middleware): 5 | pass 6 | -------------------------------------------------------------------------------- /slack_bolt/middleware/ignoring_self_events/__init__.py: -------------------------------------------------------------------------------- 1 | from .ignoring_self_events import IgnoringSelfEvents 2 | 3 | __all__ = [ 4 | "IgnoringSelfEvents", 5 | ] 6 | -------------------------------------------------------------------------------- /slack_bolt/middleware/message_listener_matches/__init__.py: -------------------------------------------------------------------------------- 1 | from .message_listener_matches import MessageListenerMatches 2 | 3 | __all__ = [ 4 | "MessageListenerMatches", 5 | ] 6 | -------------------------------------------------------------------------------- /slack_bolt/middleware/request_verification/__init__.py: -------------------------------------------------------------------------------- 1 | from .request_verification import RequestVerification 2 | 3 | __all__ = [ 4 | "RequestVerification", 5 | ] 6 | -------------------------------------------------------------------------------- /slack_bolt/middleware/ssl_check/__init__.py: -------------------------------------------------------------------------------- 1 | from .ssl_check import SslCheck 2 | 3 | __all__ = [ 4 | "SslCheck", 5 | ] 6 | -------------------------------------------------------------------------------- /slack_bolt/middleware/ssl_check/async_ssl_check.py: -------------------------------------------------------------------------------- 1 | from typing import Callable, Awaitable 2 | 3 | from .ssl_check import SslCheck 4 | from slack_bolt.middleware.async_middleware import AsyncMiddleware 5 | from slack_bolt.request.async_request import AsyncBoltRequest 6 | from slack_bolt.response import BoltResponse 7 | 8 | 9 | class AsyncSslCheck(SslCheck, AsyncMiddleware): 10 | async def async_process( 11 | self, 12 | *, 13 | req: AsyncBoltRequest, 14 | resp: BoltResponse, 15 | # As this method is not supposed to be invoked by bolt-python users, 16 | # the naming conflict with the built-in one affects 17 | # only the internals of this method 18 | next: Callable[[], Awaitable[BoltResponse]], 19 | ) -> BoltResponse: 20 | if self._is_ssl_check_request(req.body): 21 | if self._verify_token_if_needed(req.body): 22 | return self._build_error_response() 23 | return self._build_success_response() 24 | else: 25 | return await next() 26 | -------------------------------------------------------------------------------- /slack_bolt/middleware/url_verification/__init__.py: -------------------------------------------------------------------------------- 1 | from .url_verification import UrlVerification 2 | 3 | __all__ = [ 4 | "UrlVerification", 5 | ] 6 | -------------------------------------------------------------------------------- /slack_bolt/middleware/url_verification/async_url_verification.py: -------------------------------------------------------------------------------- 1 | from logging import Logger 2 | from typing import Callable, Awaitable, Optional 3 | 4 | from slack_bolt.logger import get_bolt_logger 5 | from .url_verification import UrlVerification 6 | from slack_bolt.middleware.async_middleware import AsyncMiddleware 7 | from slack_bolt.request.async_request import AsyncBoltRequest 8 | from slack_bolt.response import BoltResponse 9 | 10 | 11 | class AsyncUrlVerification(UrlVerification, AsyncMiddleware): 12 | def __init__(self, base_logger: Optional[Logger] = None): 13 | self.logger = get_bolt_logger(AsyncUrlVerification, base_logger=base_logger) 14 | 15 | async def async_process( 16 | self, 17 | *, 18 | req: AsyncBoltRequest, 19 | resp: BoltResponse, 20 | next: Callable[[], Awaitable[BoltResponse]], 21 | ) -> BoltResponse: 22 | if self._is_url_verification_request(req.body): 23 | return self._build_success_response(req.body) 24 | else: 25 | return await next() 26 | -------------------------------------------------------------------------------- /slack_bolt/oauth/__init__.py: -------------------------------------------------------------------------------- 1 | """Slack OAuth flow support for building an app that is installable in any workspaces. 2 | 3 | Refer to https://slack.dev/bolt-python/concepts#authenticating-oauth for details. 4 | """ 5 | 6 | # Don't add async module imports here 7 | from .oauth_flow import OAuthFlow 8 | 9 | __all__ = [ 10 | "OAuthFlow", 11 | ] 12 | -------------------------------------------------------------------------------- /slack_bolt/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/slack_bolt/py.typed -------------------------------------------------------------------------------- /slack_bolt/request/__init__.py: -------------------------------------------------------------------------------- 1 | """Incoming request from Slack through either HTTP request or Socket Mode connection. 2 | 3 | Refer to https://api.slack.com/apis/connections for the two types of connections. 4 | This interface encapsulates the difference between the two. 5 | """ 6 | 7 | # Don't add async module imports here 8 | from .request import BoltRequest 9 | 10 | __all__ = [ 11 | "BoltRequest", 12 | ] 13 | -------------------------------------------------------------------------------- /slack_bolt/response/__init__.py: -------------------------------------------------------------------------------- 1 | """This interface represents Bolt's synchronous response to Slack. 2 | 3 | In Socket Mode, the response data can be transformed to a WebSocket message. In the HTTP endpoint mode, 4 | the response data becomes an HTTP response data. 5 | 6 | Refer to https://api.slack.com/apis/connections for the two types of connections. 7 | """ 8 | 9 | from .response import BoltResponse 10 | 11 | __all__ = [ 12 | "BoltResponse", 13 | ] 14 | -------------------------------------------------------------------------------- /slack_bolt/util/__init__.py: -------------------------------------------------------------------------------- 1 | """Internal utilities for the Bolt framework.""" 2 | -------------------------------------------------------------------------------- /slack_bolt/util/async_utils.py: -------------------------------------------------------------------------------- 1 | from logging import Logger 2 | from typing import Optional 3 | 4 | from slack_sdk.web.async_client import AsyncWebClient 5 | 6 | from slack_bolt.version import __version__ as bolt_version 7 | 8 | 9 | def create_async_web_client(token: Optional[str] = None, logger: Optional[Logger] = None) -> AsyncWebClient: 10 | return AsyncWebClient( 11 | token=token, 12 | logger=logger, 13 | user_agent_prefix=f"Bolt-Async/{bolt_version}", 14 | ) 15 | -------------------------------------------------------------------------------- /slack_bolt/version.py: -------------------------------------------------------------------------------- 1 | """Check the latest version at https://pypi.org/project/slack-bolt/""" 2 | 3 | __version__ = "1.23.0" 4 | -------------------------------------------------------------------------------- /slack_bolt/workflows/__init__.py: -------------------------------------------------------------------------------- 1 | """Steps from apps enables developers to build their own steps. 2 | 3 | Check the following API documents first: 4 | 5 | * `slack_bolt.workflows.step.step` 6 | * `slack_bolt.workflows.step.utilities` 7 | * `slack_bolt.workflows.step.async_step` (if you use asyncio-based `AsyncApp`) 8 | 9 | Refer to https://api.slack.com/workflows/steps for details. 10 | """ 11 | -------------------------------------------------------------------------------- /slack_bolt/workflows/step/__init__.py: -------------------------------------------------------------------------------- 1 | from .step import WorkflowStep 2 | from .step_middleware import WorkflowStepMiddleware 3 | from .utilities.complete import Complete 4 | from .utilities.configure import Configure 5 | from .utilities.update import Update 6 | from .utilities.fail import Fail 7 | 8 | __all__ = [ 9 | "WorkflowStep", 10 | "WorkflowStepMiddleware", 11 | "Complete", 12 | "Configure", 13 | "Update", 14 | "Fail", 15 | ] 16 | -------------------------------------------------------------------------------- /slack_bolt/workflows/step/internals.py: -------------------------------------------------------------------------------- 1 | def _is_used_without_argument(args): 2 | """Tests if a decorator invocation is without () or (args). 3 | 4 | Args: 5 | args: arguments 6 | 7 | Returns: 8 | True if it's an invocation without args 9 | """ 10 | return len(args) == 1 11 | -------------------------------------------------------------------------------- /slack_bolt/workflows/step/utilities/__init__.py: -------------------------------------------------------------------------------- 1 | """Utilities specific to steps from apps. 2 | 3 | In steps from apps listeners, you can use a few specific listener/middleware arguments. 4 | 5 | ### `edit` listener 6 | 7 | * `slack_bolt.workflows.step.utilities.configure` for building a modal view 8 | 9 | ### `save` listener 10 | 11 | * `slack_bolt.workflows.step.utilities.update` for updating the step metadata 12 | 13 | ### `execute` listener 14 | 15 | * `slack_bolt.workflows.step.utilities.fail` for notifying the execution failure to Slack 16 | * `slack_bolt.workflows.step.utilities.complete` for notifying the execution completion to Slack 17 | 18 | For asyncio-based apps, refer to the corresponding `async` prefixed ones. 19 | """ 20 | -------------------------------------------------------------------------------- /slack_bolt/workflows/step/utilities/async_fail.py: -------------------------------------------------------------------------------- 1 | from slack_sdk.web.async_client import AsyncWebClient 2 | 3 | 4 | class AsyncFail: 5 | """`fail()` utility to tell Slack the execution failure of a step from app. 6 | 7 | async def execute(step, complete, fail): 8 | inputs = step["inputs"] 9 | # if something went wrong 10 | error = {"message": "Just testing step failure!"} 11 | await fail(error=error) 12 | 13 | ws = AsyncWorkflowStep( 14 | callback_id="add_task", 15 | edit=edit, 16 | save=save, 17 | execute=execute, 18 | ) 19 | app.step(ws) 20 | 21 | This utility is a thin wrapper of workflows.stepFailed API method. 22 | Refer to https://api.slack.com/methods/workflows.stepFailed for details. 23 | """ 24 | 25 | def __init__(self, *, client: AsyncWebClient, body: dict): 26 | self.client = client 27 | self.body = body 28 | 29 | async def __call__( 30 | self, 31 | *, 32 | error: dict, 33 | ) -> None: 34 | await self.client.workflows_stepFailed( 35 | workflow_step_execute_id=self.body["event"]["workflow_step"]["workflow_step_execute_id"], 36 | error=error, 37 | ) 38 | -------------------------------------------------------------------------------- /slack_bolt/workflows/step/utilities/fail.py: -------------------------------------------------------------------------------- 1 | from slack_sdk.web import WebClient 2 | 3 | 4 | class Fail: 5 | """`fail()` utility to tell Slack the execution failure of a step from app. 6 | 7 | def execute(step, complete, fail): 8 | inputs = step["inputs"] 9 | # if something went wrong 10 | error = {"message": "Just testing step failure!"} 11 | fail(error=error) 12 | 13 | ws = WorkflowStep( 14 | callback_id="add_task", 15 | edit=edit, 16 | save=save, 17 | execute=execute, 18 | ) 19 | app.step(ws) 20 | 21 | This utility is a thin wrapper of workflows.stepFailed API method. 22 | Refer to https://api.slack.com/methods/workflows.stepFailed for details. 23 | """ 24 | 25 | def __init__(self, *, client: WebClient, body: dict): 26 | self.client = client 27 | self.body = body 28 | 29 | def __call__( 30 | self, 31 | *, 32 | error: dict, 33 | ) -> None: 34 | self.client.workflows_stepFailed( 35 | workflow_step_execute_id=self.body["event"]["workflow_step"]["workflow_step_execute_id"], 36 | error=error, 37 | ) 38 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/__init__.py -------------------------------------------------------------------------------- /tests/adapter_tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/adapter_tests/__init__.py -------------------------------------------------------------------------------- /tests/adapter_tests/asgi/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/adapter_tests/asgi/__init__.py -------------------------------------------------------------------------------- /tests/adapter_tests/aws/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/adapter_tests/aws/__init__.py -------------------------------------------------------------------------------- /tests/adapter_tests/aws/test_lambda_s3_oauth_flow.py: -------------------------------------------------------------------------------- 1 | from slack_bolt.adapter.aws_lambda.lambda_s3_oauth_flow import LambdaS3OAuthFlow 2 | from slack_bolt.oauth.oauth_settings import OAuthSettings 3 | from tests.utils import remove_os_env_temporarily, restore_os_env 4 | 5 | try: 6 | from moto import mock_aws 7 | except ImportError: 8 | from moto import mock_s3 as mock_aws 9 | 10 | 11 | class TestLambdaS3OAuthFlow: 12 | def setup_method(self): 13 | self.old_os_env = remove_os_env_temporarily() 14 | 15 | def teardown_method(self): 16 | restore_os_env(self.old_os_env) 17 | 18 | @mock_aws 19 | def test_instantiation(self): 20 | oauth_flow = LambdaS3OAuthFlow( 21 | settings=OAuthSettings( 22 | client_id="111.222", 23 | client_secret="xxx", 24 | scopes=["chat:write"], 25 | ), 26 | installation_bucket_name="dummy-installation", 27 | oauth_state_bucket_name="dummy-state", 28 | ) 29 | assert oauth_flow is not None 30 | assert oauth_flow.client is not None 31 | assert oauth_flow.logger is not None 32 | -------------------------------------------------------------------------------- /tests/adapter_tests/bottle/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/adapter_tests/bottle/__init__.py -------------------------------------------------------------------------------- /tests/adapter_tests/bottle/test_bottle_oauth.py: -------------------------------------------------------------------------------- 1 | from slack_bolt.adapter.bottle import SlackRequestHandler 2 | from slack_bolt.app import App 3 | from slack_bolt.oauth.oauth_settings import OAuthSettings 4 | 5 | signing_secret = "secret" 6 | app = App( 7 | signing_secret=signing_secret, 8 | oauth_settings=OAuthSettings( 9 | client_id="111.111", 10 | client_secret="xxx", 11 | scopes=["chat:write", "commands"], 12 | ), 13 | ) 14 | handler = SlackRequestHandler(app) 15 | 16 | from bottle import get, request, response 17 | from boddle import boddle 18 | 19 | 20 | @get("/slack/install") 21 | def install(): 22 | return handler.handle(request, response) 23 | 24 | 25 | class TestBottle: 26 | def test_oauth(self): 27 | with boddle(method="GET", path="/slack/install"): 28 | response_body = install() 29 | assert response.status_code == 200 30 | assert response.headers.get("content-type") == "text/html; charset=utf-8" 31 | assert "https://slack.com/oauth/v2/authorize?state=" in response_body 32 | -------------------------------------------------------------------------------- /tests/adapter_tests/cherrypy/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/adapter_tests/cherrypy/__init__.py -------------------------------------------------------------------------------- /tests/adapter_tests/django/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/adapter_tests/django/__init__.py -------------------------------------------------------------------------------- /tests/adapter_tests/django/test_django_settings.py: -------------------------------------------------------------------------------- 1 | SECRET_KEY = "XXX" 2 | DATABASES = { 3 | "default": { 4 | "ENGINE": "django.db.backends.sqlite3", 5 | "NAME": "logs/db.sqlite3", 6 | } 7 | } 8 | # Django 4 warning: The default value of USE_TZ will change from False to True in Django 5.0. 9 | # Set USE_TZ to False in your project settings if you want to keep the current default behavior. 10 | USE_TZ = False 11 | -------------------------------------------------------------------------------- /tests/adapter_tests/falcon/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/adapter_tests/falcon/__init__.py -------------------------------------------------------------------------------- /tests/adapter_tests/flask/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/adapter_tests/flask/__init__.py -------------------------------------------------------------------------------- /tests/adapter_tests/google_cloud_functions/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/adapter_tests/google_cloud_functions/__init__.py -------------------------------------------------------------------------------- /tests/adapter_tests/pyramid/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/adapter_tests/pyramid/__init__.py -------------------------------------------------------------------------------- /tests/adapter_tests/socket_mode/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/adapter_tests/socket_mode/__init__.py -------------------------------------------------------------------------------- /tests/adapter_tests/starlette/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/adapter_tests/starlette/__init__.py -------------------------------------------------------------------------------- /tests/adapter_tests/tornado/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/adapter_tests/tornado/__init__.py -------------------------------------------------------------------------------- /tests/adapter_tests/wsgi/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/adapter_tests/wsgi/__init__.py -------------------------------------------------------------------------------- /tests/adapter_tests_async/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/adapter_tests_async/__init__.py -------------------------------------------------------------------------------- /tests/adapter_tests_async/socket_mode/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/adapter_tests_async/socket_mode/__init__.py -------------------------------------------------------------------------------- /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 = {} 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/scenario_tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/scenario_tests/__init__.py -------------------------------------------------------------------------------- /tests/scenario_tests_async/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/scenario_tests_async/__init__.py -------------------------------------------------------------------------------- /tests/slack_bolt/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt/__init__.py -------------------------------------------------------------------------------- /tests/slack_bolt/app/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt/app/__init__.py -------------------------------------------------------------------------------- /tests/slack_bolt/app/test_dev_server.py: -------------------------------------------------------------------------------- 1 | from slack_sdk import WebClient 2 | 3 | from slack_bolt.app.app import SlackAppDevelopmentServer, App 4 | from tests.mock_web_api_server import ( 5 | setup_mock_web_api_server, 6 | cleanup_mock_web_api_server, 7 | ) 8 | from tests.utils import remove_os_env_temporarily, restore_os_env 9 | 10 | 11 | class TestDevServer: 12 | signing_secret = "secret" 13 | valid_token = "xoxb-valid" 14 | mock_api_server_base_url = "http://localhost:8888" 15 | web_client = WebClient( 16 | token=valid_token, 17 | base_url=mock_api_server_base_url, 18 | ) 19 | 20 | def setup_method(self): 21 | self.old_os_env = remove_os_env_temporarily() 22 | setup_mock_web_api_server(self) 23 | 24 | def teardown_method(self): 25 | cleanup_mock_web_api_server(self) 26 | restore_os_env(self.old_os_env) 27 | 28 | def test_instance(self): 29 | server = SlackAppDevelopmentServer( 30 | port=3001, 31 | path="/slack/events", 32 | app=App(signing_secret=self.signing_secret, client=self.web_client), 33 | ) 34 | assert server is not None 35 | -------------------------------------------------------------------------------- /tests/slack_bolt/authorization/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt/authorization/__init__.py -------------------------------------------------------------------------------- /tests/slack_bolt/context/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt/context/__init__.py -------------------------------------------------------------------------------- /tests/slack_bolt/context/test_complete.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from slack_sdk import WebClient 4 | from slack_bolt.context.complete import Complete 5 | from tests.mock_web_api_server import ( 6 | setup_mock_web_api_server, 7 | cleanup_mock_web_api_server, 8 | ) 9 | 10 | 11 | class TestComplete: 12 | def setup_method(self): 13 | setup_mock_web_api_server(self) 14 | valid_token = "xoxb-valid" 15 | mock_api_server_base_url = "http://localhost:8888" 16 | self.web_client = WebClient(token=valid_token, base_url=mock_api_server_base_url) 17 | 18 | def teardown_method(self): 19 | cleanup_mock_web_api_server(self) 20 | 21 | def test_complete(self): 22 | complete_success = Complete(client=self.web_client, function_execution_id="fn1111") 23 | 24 | response = complete_success(outputs={"key": "value"}) 25 | 26 | assert response.status_code == 200 27 | 28 | def test_complete_no_function_execution_id(self): 29 | complete = Complete(client=self.web_client, function_execution_id=None) 30 | 31 | with pytest.raises(ValueError): 32 | complete(outputs={"key": "value"}) 33 | -------------------------------------------------------------------------------- /tests/slack_bolt/context/test_fail.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from slack_sdk import WebClient 4 | from slack_bolt.context.fail import Fail 5 | from tests.mock_web_api_server import ( 6 | setup_mock_web_api_server, 7 | cleanup_mock_web_api_server, 8 | ) 9 | 10 | 11 | class TestFail: 12 | def setup_method(self): 13 | setup_mock_web_api_server(self) 14 | valid_token = "xoxb-valid" 15 | mock_api_server_base_url = "http://localhost:8888" 16 | self.web_client = WebClient(token=valid_token, base_url=mock_api_server_base_url) 17 | 18 | def teardown_method(self): 19 | cleanup_mock_web_api_server(self) 20 | 21 | def test_fail(self): 22 | fail = Fail(client=self.web_client, function_execution_id="fn1111") 23 | 24 | response = fail(error="something went wrong") 25 | 26 | assert response.status_code == 200 27 | 28 | def test_fail_no_function_execution_id(self): 29 | fail = Fail(client=self.web_client, function_execution_id=None) 30 | 31 | with pytest.raises(ValueError): 32 | fail(error="there was an error") 33 | -------------------------------------------------------------------------------- /tests/slack_bolt/error/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt/error/__init__.py -------------------------------------------------------------------------------- /tests/slack_bolt/error/test_errors.py: -------------------------------------------------------------------------------- 1 | from slack_bolt import BoltRequest 2 | from slack_bolt.error import BoltUnhandledRequestError 3 | 4 | 5 | class TestErrors: 6 | def setup_method(self): 7 | pass 8 | 9 | def teardown_method(self): 10 | pass 11 | 12 | def test_say(self): 13 | request = BoltRequest(body="foo=bar") 14 | exception = BoltUnhandledRequestError( 15 | request=request, 16 | current_response={}, 17 | last_global_middleware_name="last_middleware", 18 | ) 19 | assert str(exception) == "unhandled request error" 20 | -------------------------------------------------------------------------------- /tests/slack_bolt/kwargs_injection/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt/kwargs_injection/__init__.py -------------------------------------------------------------------------------- /tests/slack_bolt/listener/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt/listener/__init__.py -------------------------------------------------------------------------------- /tests/slack_bolt/listener/test_listener_completion_handler.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from slack_bolt import BoltRequest 4 | from slack_bolt.listener.listener_completion_handler import ( 5 | CustomListenerCompletionHandler, 6 | ) 7 | 8 | 9 | class TestListenerCompletionHandler: 10 | def test_handler(self): 11 | result = {"called": False} 12 | 13 | def f(): 14 | result["called"] = True 15 | 16 | handler = CustomListenerCompletionHandler( 17 | logger=logging.getLogger(__name__), 18 | func=f, 19 | ) 20 | request = BoltRequest( 21 | body="{}", 22 | query={}, 23 | headers={"content-type": ["application/json"]}, 24 | context={}, 25 | ) 26 | handler.handle(request, None) 27 | assert result["called"] is True 28 | -------------------------------------------------------------------------------- /tests/slack_bolt/listener/test_listener_start_handler.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from slack_bolt import BoltRequest 4 | from slack_bolt.listener.listener_start_handler import ( 5 | CustomListenerStartHandler, 6 | ) 7 | 8 | 9 | class TestListenerStartHandler: 10 | def test_handler(self): 11 | result = {"called": False} 12 | 13 | def f(): 14 | result["called"] = True 15 | 16 | handler = CustomListenerStartHandler( 17 | logger=logging.getLogger(__name__), 18 | func=f, 19 | ) 20 | request = BoltRequest( 21 | body="{}", 22 | query={}, 23 | headers={"content-type": ["application/json"]}, 24 | context={}, 25 | ) 26 | handler.handle(request, None) 27 | assert result["called"] is True 28 | -------------------------------------------------------------------------------- /tests/slack_bolt/listener_matcher/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt/listener_matcher/__init__.py -------------------------------------------------------------------------------- /tests/slack_bolt/listener_matcher/test_custom_listener_matcher.py: -------------------------------------------------------------------------------- 1 | from slack_bolt import BoltRequest, BoltResponse, CustomListenerMatcher 2 | from slack_bolt.listener_matcher import ListenerMatcher 3 | 4 | 5 | def func(body, request, response, dummy): 6 | assert body is not None 7 | assert request is not None 8 | assert response is not None 9 | assert dummy is None 10 | return body["result"] 11 | 12 | 13 | class TestCustomListenerMatcher: 14 | def setup_method(self): 15 | pass 16 | 17 | def teardown_method(self): 18 | pass 19 | 20 | def test_instantiation(self): 21 | matcher: ListenerMatcher = CustomListenerMatcher( 22 | app_name="foo", 23 | func=func, 24 | ) 25 | resp = BoltResponse(status=201) 26 | 27 | req = BoltRequest(body='payload={"result":true}') 28 | result = matcher.matches(req, resp) 29 | assert result is True 30 | 31 | req = BoltRequest(body='payload={"result":false}') 32 | result = matcher.matches(req, resp) 33 | assert result is False 34 | -------------------------------------------------------------------------------- /tests/slack_bolt/logger/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt/logger/__init__.py -------------------------------------------------------------------------------- /tests/slack_bolt/middleware/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt/middleware/__init__.py -------------------------------------------------------------------------------- /tests/slack_bolt/middleware/authorization/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt/middleware/authorization/__init__.py -------------------------------------------------------------------------------- /tests/slack_bolt/middleware/request_verification/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt/middleware/request_verification/__init__.py -------------------------------------------------------------------------------- /tests/slack_bolt/oauth/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt/oauth/__init__.py -------------------------------------------------------------------------------- /tests/slack_bolt/request/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt/request/__init__.py -------------------------------------------------------------------------------- /tests/slack_bolt/util/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt/util/__init__.py -------------------------------------------------------------------------------- /tests/slack_bolt/workflows/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt/workflows/__init__.py -------------------------------------------------------------------------------- /tests/slack_bolt/workflows/step/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt/workflows/step/__init__.py -------------------------------------------------------------------------------- /tests/slack_bolt/workflows/step/test_step.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from slack_bolt import Ack 4 | from slack_bolt.error import BoltError 5 | from slack_bolt.workflows.step import WorkflowStep 6 | 7 | 8 | class TestStep: 9 | def test_build(self): 10 | step = WorkflowStep.builder("foo") 11 | step.edit(just_ack) 12 | step.save(just_ack) 13 | step.execute(just_ack) 14 | assert step.build() is not None 15 | 16 | def test_build_errors(self): 17 | with pytest.raises(BoltError): 18 | step = WorkflowStep.builder("foo") 19 | step.save(just_ack) 20 | step.execute(just_ack) 21 | step.build() 22 | with pytest.raises(BoltError): 23 | step = WorkflowStep.builder("foo") 24 | step.edit(just_ack) 25 | step.execute(just_ack) 26 | step.build() 27 | with pytest.raises(BoltError): 28 | step = WorkflowStep.builder("foo") 29 | step.edit(just_ack) 30 | step.save(just_ack) 31 | step.build() 32 | 33 | 34 | def just_ack(ack: Ack): 35 | ack() 36 | 37 | 38 | def execute(): 39 | pass 40 | -------------------------------------------------------------------------------- /tests/slack_bolt_async/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt_async/__init__.py -------------------------------------------------------------------------------- /tests/slack_bolt_async/app/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt_async/app/__init__.py -------------------------------------------------------------------------------- /tests/slack_bolt_async/app/test_server.py: -------------------------------------------------------------------------------- 1 | from slack_bolt.app.async_app import AsyncApp 2 | from slack_bolt.app.async_server import AsyncSlackAppServer 3 | 4 | 5 | class TestServer: 6 | def setup_method(self): 7 | pass 8 | 9 | def teardown_method(self): 10 | pass 11 | 12 | def test_instance(self): 13 | server = AsyncSlackAppServer( 14 | port=3001, 15 | path="/slack/events", 16 | app=AsyncApp( 17 | signing_secret="valid", 18 | token="xoxb-valid", 19 | ), 20 | ) 21 | assert server is not None 22 | -------------------------------------------------------------------------------- /tests/slack_bolt_async/authorization/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt_async/authorization/__init__.py -------------------------------------------------------------------------------- /tests/slack_bolt_async/context/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt_async/context/__init__.py -------------------------------------------------------------------------------- /tests/slack_bolt_async/kwargs_injection/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt_async/kwargs_injection/__init__.py -------------------------------------------------------------------------------- /tests/slack_bolt_async/listener/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt_async/listener/__init__.py -------------------------------------------------------------------------------- /tests/slack_bolt_async/listener/test_async_listener_completion_handler.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import pytest 3 | 4 | from slack_bolt.request.async_request import AsyncBoltRequest 5 | from slack_bolt.listener.async_listener_completion_handler import ( 6 | AsyncCustomListenerCompletionHandler, 7 | ) 8 | 9 | 10 | class TestAsyncListenerCompletionHandler: 11 | @pytest.mark.asyncio 12 | async def test_handler(self): 13 | result = {"called": False} 14 | 15 | async def f(): 16 | result["called"] = True 17 | 18 | handler = AsyncCustomListenerCompletionHandler( 19 | logger=logging.getLogger(__name__), 20 | func=f, 21 | ) 22 | request = AsyncBoltRequest( 23 | body="{}", 24 | query={}, 25 | headers={"content-type": ["application/json"]}, 26 | context={}, 27 | ) 28 | await handler.handle(request, None) 29 | assert result["called"] is True 30 | -------------------------------------------------------------------------------- /tests/slack_bolt_async/listener/test_async_listener_start_handler.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import pytest 3 | 4 | from slack_bolt.request.async_request import AsyncBoltRequest 5 | from slack_bolt.listener.async_listener_start_handler import ( 6 | AsyncCustomListenerStartHandler, 7 | ) 8 | 9 | 10 | class TestAsyncListenerStartHandler: 11 | @pytest.mark.asyncio 12 | async def test_handler(self): 13 | result = {"called": False} 14 | 15 | async def f(): 16 | result["called"] = True 17 | 18 | handler = AsyncCustomListenerStartHandler( 19 | logger=logging.getLogger(__name__), 20 | func=f, 21 | ) 22 | request = AsyncBoltRequest( 23 | body="{}", 24 | query={}, 25 | headers={"content-type": ["application/json"]}, 26 | context={}, 27 | ) 28 | await handler.handle(request, None) 29 | assert result["called"] is True 30 | -------------------------------------------------------------------------------- /tests/slack_bolt_async/listener_matcher/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt_async/listener_matcher/__init__.py -------------------------------------------------------------------------------- /tests/slack_bolt_async/listener_matcher/test_async_custom_listener_matcher.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from slack_bolt.listener_matcher.async_listener_matcher import ( 4 | AsyncCustomListenerMatcher, 5 | AsyncListenerMatcher, 6 | ) 7 | from slack_bolt.request.async_request import AsyncBoltRequest 8 | from slack_bolt.response import BoltResponse 9 | 10 | 11 | async def func(body, request, response, dummy): 12 | assert body is not None 13 | assert request is not None 14 | assert response is not None 15 | assert dummy is None 16 | return body["result"] 17 | 18 | 19 | class TestAsyncCustomListenerMatcher: 20 | @pytest.mark.asyncio 21 | async def test_instantiation(self): 22 | matcher: AsyncListenerMatcher = AsyncCustomListenerMatcher( 23 | app_name="foo", 24 | func=func, 25 | ) 26 | resp = BoltResponse(status=201) 27 | 28 | req = AsyncBoltRequest(body='payload={"result":true}') 29 | result = await matcher.async_matches(req, resp) 30 | assert result is True 31 | 32 | req = AsyncBoltRequest(body='payload={"result":false}') 33 | result = await matcher.async_matches(req, resp) 34 | assert result is False 35 | -------------------------------------------------------------------------------- /tests/slack_bolt_async/logger/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt_async/logger/__init__.py -------------------------------------------------------------------------------- /tests/slack_bolt_async/middleware/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt_async/middleware/__init__.py -------------------------------------------------------------------------------- /tests/slack_bolt_async/middleware/authorization/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt_async/middleware/authorization/__init__.py -------------------------------------------------------------------------------- /tests/slack_bolt_async/middleware/request_verification/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt_async/middleware/request_verification/__init__.py -------------------------------------------------------------------------------- /tests/slack_bolt_async/oauth/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt_async/oauth/__init__.py -------------------------------------------------------------------------------- /tests/slack_bolt_async/request/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt_async/request/__init__.py -------------------------------------------------------------------------------- /tests/slack_bolt_async/request/test_async_request.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from slack_bolt.request.async_request import AsyncBoltRequest 4 | 5 | 6 | class TestAsyncRequest: 7 | @pytest.mark.asyncio 8 | async def test_all_none_values_http(self): 9 | req = AsyncBoltRequest(body=None, headers=None, query=None, context=None) 10 | assert req is not None 11 | assert req.raw_body == "" 12 | assert req.body == {} 13 | 14 | @pytest.mark.asyncio 15 | async def test_all_none_values_socket_mode(self): 16 | req = AsyncBoltRequest(body=None, headers=None, query=None, context=None, mode="socket_mode") 17 | assert req is not None 18 | assert req.raw_body == "" 19 | assert req.body == {} 20 | -------------------------------------------------------------------------------- /tests/slack_bolt_async/workflows/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt_async/workflows/__init__.py -------------------------------------------------------------------------------- /tests/slack_bolt_async/workflows/step/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt_async/workflows/step/__init__.py -------------------------------------------------------------------------------- /tests/slack_bolt_async/workflows/step/test_async_step.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from slack_bolt import Ack 4 | from slack_bolt.error import BoltError 5 | from slack_bolt.workflows.step.async_step import AsyncWorkflowStep 6 | 7 | 8 | class TestStep: 9 | def test_build(self): 10 | step = AsyncWorkflowStep.builder("foo") 11 | step.edit(just_ack) 12 | step.save(just_ack) 13 | step.execute(just_ack) 14 | assert step.build() is not None 15 | 16 | def test_build_errors(self): 17 | with pytest.raises(BoltError): 18 | step = AsyncWorkflowStep.builder("foo") 19 | step.save(just_ack) 20 | step.execute(just_ack) 21 | step.build() 22 | with pytest.raises(BoltError): 23 | step = AsyncWorkflowStep.builder("foo") 24 | step.edit(just_ack) 25 | step.execute(just_ack) 26 | step.build() 27 | with pytest.raises(BoltError): 28 | step = AsyncWorkflowStep.builder("foo") 29 | step.edit(just_ack) 30 | step.save(just_ack) 31 | step.build() 32 | 33 | 34 | def just_ack(ack: Ack): 35 | ack() 36 | 37 | 38 | def execute(): 39 | pass 40 | -------------------------------------------------------------------------------- /tests/utils.py: -------------------------------------------------------------------------------- 1 | import os 2 | import asyncio 3 | 4 | 5 | def remove_os_env_temporarily() -> dict: 6 | old_env = os.environ.copy() 7 | os.environ.clear() 8 | for key, value in old_env.items(): 9 | if key.startswith("BOLT_PYTHON_"): 10 | os.environ[key] = value 11 | return old_env 12 | 13 | 14 | def restore_os_env(old_env: dict) -> None: 15 | os.environ.update(old_env) 16 | 17 | 18 | def get_event_loop(): 19 | try: 20 | return asyncio.get_event_loop() 21 | except RuntimeError as ex: 22 | if "There is no current event loop in thread" in str(ex): 23 | loop = asyncio.new_event_loop() 24 | asyncio.set_event_loop(loop) 25 | return loop 26 | --------------------------------------------------------------------------------